diff --git a/README.md b/README.md index 71f7860e..b70e1cd6 100644 --- a/README.md +++ b/README.md @@ -323,3 +323,20 @@ git merge upstream/master Alternatively, changes from other upstreams (forks) and branches can be merged like that in the same way. + +## PCDU +Connect to serial console of P60 Dock +```` +picocom -b 500000 /dev/ttyUSBx +```` +General information +```` +cmp ident +```` +List parameter table: +x values: 1,2 or 4 +```` +param table x +```` +Table 4 lists HK parameters + diff --git a/gomspace/gomspace.mk b/gomspace/gomspace.mk index 57d38da7..8a781ac8 100644 --- a/gomspace/gomspace.mk +++ b/gomspace/gomspace.mk @@ -6,6 +6,8 @@ CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/crypto/*.c) CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/arch/posix/*.c) CSRC += $(wildcard $(CURRENTPATH)/libcsp/src/transport/*.c) CSRC += $(wildcard $(CURRENTPATH)/p60-dock_client/src/*.c) +CSRC += $(wildcard $(CURRENTPATH)/libutil/src/*.c) +CSRC += $(wildcard $(CURRENTPATH)/libutil/src/linux/*.c) CSRC += $(wildcard $(CURRENTPATH)/libparam_client/src/*.c) CSRC += $(wildcard $(CURRENTPATH)/libparam_client/src/rparam/*.c) @@ -16,4 +18,5 @@ INCLUDES += $(CURRENTPATH)/p60-dock_client/include/gs/p60-dock/param INCLUDES += $(CURRENTPATH)/libparam_client/include INCLUDES += $(CURRENTPATH)/libparam_client/include/deprecated INCLUDES += $(CURRENTPATH)/libp60_client/include -INCLUDES += $(CURRENTPATH)/libutil/include \ No newline at end of file +INCLUDES += $(CURRENTPATH)/libutil/include +INCLUDES += $(CURRENTPATH)/libgscsp/include \ No newline at end of file diff --git a/gomspace/libgscsp/include/gs/csp/address.h b/gomspace/libgscsp/include/gs/csp/address.h new file mode 100644 index 00000000..9d6e701c --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/address.h @@ -0,0 +1,65 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_ADDRESS_H +#define LIBGSCSP_INCLUDE_GS_CSP_ADDRESS_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Default CSP addresses for nodes in the satellite - often changed for each project. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default address for OBC (On Board Computer). + Example: a3200-sdk. +*/ +#define GS_CSP_ADDR_OBC 1 +/** + Power supply. + Example: nano-power. +*/ +#define GS_CSP_ADDR_EPS 2 +/** + Payload address. +*/ +#define GS_CSP_ADDR_PAYLOAD_3 3 +/** + Default address for ADCS. + Example: a3200-adcs. +*/ +#define GS_CSP_ADDR_ADCS 4 +/** + Default address for radio. + Example: nanocom-ax. +*/ +#define GS_CSP_ADDR_NANOCOM 5 +/** + Payload address. +*/ +#define GS_CSP_ADDR_PAYLOAD_6 6 +/** + Battery pack. + Example: nano-power-bpx. +*/ +#define GS_CSP_ADDR_BPX 7 +/** + Payload address. +*/ +#define GS_CSP_ADDR_PAYLOAD_8 8 +/** + Reaction wheels. + Example: gsw-600. +*/ +#define GS_CSP_ADDR_GSW600 9 +/** + Antenna module. + Example: ant2150. +*/ +#define GS_CSP_ADDR_ANT2150 10 + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/command.h b/gomspace/libgscsp/include/gs/csp/command.h new file mode 100644 index 00000000..79d518db --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/command.h @@ -0,0 +1,25 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_COMMAND_H +#define LIBGSCSP_INCLUDE_GS_CSP_COMMAND_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP commands. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Register CSP commands. + @return_gs_error_t +*/ +gs_error_t gs_csp_register_commands(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/conn.h b/gomspace/libgscsp/include/gs/csp/conn.h new file mode 100644 index 00000000..c8bd755e --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/conn.h @@ -0,0 +1,26 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_CONN_H +#define LIBGSCSP_INCLUDE_GS_CSP_CONN_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Extensions to standard libcsp connection interface. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Return number of open connections. + + @return number of open connections. +*/ +size_t gs_csp_conn_get_open(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/csp.h b/gomspace/libgscsp/include/gs/csp/csp.h new file mode 100644 index 00000000..6268bced --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/csp.h @@ -0,0 +1,202 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_CSP_H +#define LIBGSCSP_INCLUDE_GS_CSP_CSP_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Extensions to standard libcsp. +*/ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default CSP timeout (mS). + + Default timeout value for communicating with a satellite, based on round-trip time of approximately 500 mS. +*/ +#define GS_CSP_TIMEOUT 3000 + +/** + Check if address is a valid CSP adddress. + @param[in] address CSP address to verify + @return \a true if address is valid. +*/ +bool gs_csp_is_address_valid(uint8_t address); + +/** + gscsp and csp configuration. +*/ +typedef struct { + /** + Forward CSP logs to GomSpace log (libutil). + */ + bool use_gs_log; + + /** + Use command line options. + Configure interfaces specified on the command line. + @note Only supported on Linux - ignored on platforms without command line options. + */ + bool use_command_line_options; + + /** + Size of a CSP buffer (in bytes). + @see csp_buffer_init(). + */ + size_t csp_buffer_size; + /** + Number of CSP buffers to allocate. + @see csp_buffer_init(). + */ + size_t csp_buffers; + + /** + CSP address of the system + */ + uint8_t address; + /** + Host name, returned by the #CSP_CMP_IDENT request. + @note String must remain valid as long as the application is running. + */ + const char *hostname; + /** + Model, returned by the #CSP_CMP_IDENT request. + @note String must remain valid as long as the application is running. + */ + const char *model; + /** + Revision, returned by the #CSP_CMP_IDENT request + @note String must remain valid as long as the application is running. + */ + const char *revision; + +} gs_csp_conf_t; + +/** + Get default CSP configuration for server systems. + @param[in] conf user supplied configuration struct. +*/ +void gs_csp_conf_get_defaults_server(gs_csp_conf_t * conf); + +/** + Get default CSP configuration for embedded systems. + @param[in] conf user supplied configuration struct. +*/ +void gs_csp_conf_get_defaults_embedded(gs_csp_conf_t * conf); + +/** + Initialize CSP. + + Wraps initialization of libcsp, by calling following functions + - hooks CSP log system into GomSpace log system. + - initializes CSP buffers by calling csp_buffer_init() + - initializes CSP by calling csp_init() + - configure interfaces specificed on command line + - configure routing table specificed on command line, or default if only one interface is present. + + @param[in] conf configuration. + @return_gs_error_t +*/ +gs_error_t gs_csp_init(const gs_csp_conf_t * conf); + +/** + Perform an entire request/reply transaction, + + Copies both input buffer and reply to output buffer. + Also makes the connection and closes it again + Differ from 'csp_transaction()' by limiting input buffer size when input length is unknown + + @param[in] prio CSP Prio + @param[in] dest CSP Dest + @param[in] port CSP Port + @param[in] timeout timeout in ms + @param[in] tx_buf pointer to outgoing data buffer + @param[in] tx_len length of request to send + @param[out] rx_buf pointer to incoming data buffer + @param[in] rx_max_len length of expected reply (input buffer size) + @param[out] rx_len pointer to length of reply + @param[in] opts connection options. + @return GS_ERROR_OVERFLOW when input length larger than buffer - it still updates rx_buf + @return GS_ERROR_IO when 'csp_send()' or 'csp_read()' fails + @return GS_ERROR_ALLOC when 'csp_get_buffer()' fails + @return_gs_error_t + */ +gs_error_t gs_csp_transaction2(uint8_t prio, + uint8_t dest, + uint8_t port, + uint32_t timeout, + const void * tx_buf, + size_t tx_len, + void * rx_buf, + size_t rx_max_len, + size_t * rx_len, + uint32_t opts); + +/** + Perform an entire request/reply transaction, + + @param[in] prio CSP Prio + @param[in] dest CSP Dest + @param[in] port CSP Port + @param[in] timeout timeout in ms + @param[in] tx_buf pointer to outgoing data buffer + @param[in] tx_len length of request to send + @param[out] rx_buf pointer to incoming data buffer + @param[in] rx_max_len length of expected reply (input buffer size) + @param[out] rx_len pointer to length of reply + @return GS_ERROR_OVERFLOW when input length larger than buffer - it still updates rx_buf + @return GS_ERROR_IO when 'csp_send()' or 'csp_read()' fails + @return GS_ERROR_ALLOC when 'csp_get_buffer()' fails + @return_gs_error_t + @see gs_csp_transaction2() +*/ +static inline gs_error_t gs_csp_transaction(uint8_t prio, + uint8_t dest, + uint8_t port, + uint32_t timeout, + const void * tx_buf, + size_t tx_len, + void * rx_buf, + size_t rx_max_len, + size_t * rx_len) +{ + return gs_csp_transaction2(prio, dest, port, timeout, tx_buf, tx_len, rx_buf, rx_max_len, rx_len, 0); +} + +/** + Use an existing connection to perform a transaction. + + This is only possible if the next packet is on the same port and destination! + Differ from 'csp_transaction_persistent()' by limiting input buffer size when input length is unknown + + @param[in] conn pointer to connection structure + @param[in] timeout timeout in ms + @param[in] tx_buf pointer to outgoing data buffer + @param[in] tx_len length of request to send + @param[out] rx_buf pointer to incoming data buffer + @param[in] rx_max_len length of expected reply (input buffer size) + @param[out] rx_len pointer to length of reply + @return GS_ERROR_OVERFLOW when input length larger than buffer - it still updates rx_buf + @return GS_ERROR_IO when 'csp_send()' or 'csp_read()' fails + @return GS_ERROR_ALLOC when 'csp_get_buffer()' fails + @return_gs_error_t + */ +gs_error_t gs_csp_transaction_persistent(csp_conn_t * conn, + uint32_t timeout, + const void * tx_buf, + size_t tx_len, + void * rx_buf, + size_t rx_max_len, + size_t * rx_len); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/drivers/can/can.h b/gomspace/libgscsp/include/gs/csp/drivers/can/can.h new file mode 100644 index 00000000..453dabde --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/drivers/can/can.h @@ -0,0 +1,54 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_CAN_CAN_H +#define LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_CAN_CAN_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + GomSpace CAN API for CSP. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Default CAN interface name. +*/ +#define GS_CSP_CAN_DEFAULT_IF_NAME "CAN" + +/** + Initialize CSP for CAN device. + + @param[in] device CAN device + @param[in] csp_addr CSP address. + @param[in] mtu MTU, normally CSP_CAN_MTU. + @param[in] name name of CSP interface, if NULL #GS_CSP_CAN_DEFAULT_IF_NAME will be used. + @param[in] set_default_route if \a true, the device will be set as default route. + @param[out] csp_if the added CSP interface. + @return #GS_ERROR_EXIST if device already exists. + @return_gs_error_t +*/ +gs_error_t gs_csp_can_init2(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, bool set_default_route, csp_iface_t ** csp_if); + +/** + Initialize CSP for CAN device. + + @param[in] device CAN device + @param[in] csp_addr CSP address. + @param[in] mtu MTU, normally CSP_CAN_MTU. + @param[in] name name of CSP interface, if NULL #GS_CSP_CAN_DEFAULT_IF_NAME will be used. + @param[out] csp_if the added CSP interface. + @return #GS_ERROR_EXIST if device already exists. + @return_gs_error_t + @see gs_csp_can_init2() +*/ +gs_error_t gs_csp_can_init(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, csp_iface_t ** csp_if); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/drivers/i2c/i2c.h b/gomspace/libgscsp/include/gs/csp/drivers/i2c/i2c.h new file mode 100644 index 00000000..4b6204f1 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/drivers/i2c/i2c.h @@ -0,0 +1,29 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_I2C_I2C_H +#define LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_I2C_I2C_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + GomSpace I2C API for CSP. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Initialize CSP for I2C device. + @note I2C device must be initialized allready with the same I2C address as the CSP address. + + @param[in] device I2C device. + @param[in] csp_addr CSP address. + @return_gs_error_t +*/ +gs_error_t gs_csp_i2c_init(uint8_t device, uint8_t csp_addr); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/drivers/kiss/kiss.h b/gomspace/libgscsp/include/gs/csp/drivers/kiss/kiss.h new file mode 100644 index 00000000..cb098fce --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/drivers/kiss/kiss.h @@ -0,0 +1,30 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_KISS_KISS_H +#define LIBGSCSP_INCLUDE_GS_CSP_DRIVERS_KISS_KISS_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +/** + @file + + GomSpace KISS API for CSP. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Initialize CSP for KISS device. + @note KISS/UART device must be initialized all ready. + + @param[in] device KISS/UART device. + @return_gs_error_t +*/ +gs_error_t gs_csp_kiss_init(uint8_t device); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/error.h b/gomspace/libgscsp/include/gs/csp/error.h new file mode 100644 index 00000000..8de40787 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/error.h @@ -0,0 +1,28 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_ERROR_H +#define LIBGSCSP_INCLUDE_GS_CSP_ERROR_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Converting CSP error codes to #gs_error_t +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Convert CSP error. + + @param[in] csp_error CSP error + @return GS error code representing the CSP error. +*/ +gs_error_t gs_csp_error(int csp_error); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/linux/command_line.h b/gomspace/libgscsp/include/gs/csp/linux/command_line.h new file mode 100644 index 00000000..e416eda5 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/linux/command_line.h @@ -0,0 +1,24 @@ +#ifndef GS_CSP_LINUX_COMMAND_LINE_H +#define GS_CSP_LINUX_COMMAND_LINE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Command line support. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Command line options. +*/ +extern const struct argp_child gs_csp_command_line_options; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/log.h b/gomspace/libgscsp/include/gs/csp/log.h new file mode 100644 index 00000000..91360cc0 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/log.h @@ -0,0 +1,27 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_LOG_H +#define LIBGSCSP_INCLUDE_GS_CSP_LOG_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP log hook. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Hook into CSP log system. + All CSP logs will be logged through Log (libutil). + + @return_gs_error_t +*/ +gs_error_t gs_csp_log_init(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/port.h b/gomspace/libgscsp/include/gs/csp/port.h new file mode 100644 index 00000000..29fa32e4 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/port.h @@ -0,0 +1,111 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_PORT_H +#define LIBGSCSP_INCLUDE_GS_CSP_PORT_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + Port definitions for standard CSP and GomSpace services. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Port definitions for standard CSP and GomSpace services. +*/ +typedef enum { + /** + CSP Management Protocol - standard CSP service. + */ + GS_CSP_CMP = CSP_CMP, // 0 + /** + Ping - standard CSP service. + */ + GS_CSP_PING = CSP_PING, // 1 + /** + Show process status - standard CSP service. + */ + GS_CSP_PS = CSP_PS, // 2 + /** + Show memory free - standard CSP service. + */ + GS_CSP_MEM_FREE = CSP_MEMFREE, // 3 + GS_CSP_MEMFREE = GS_CSP_MEM_FREE, + /** + Reboot/reset request - standard CSP service. + */ + GS_CSP_REBOOT = CSP_REBOOT, // 4 + /** + Show number of free CSP buffers - standard CSP service. + */ + GS_CSP_BUF_FREE = CSP_BUF_FREE, // 5 + /** + Show uptime (time since last reset) - standard CSP service. + */ + GS_CSP_UPTIME = CSP_UPTIME, // 6 + /** + Parameter service (libparam) + */ + GS_CSP_PORT_RPARAM = 7, + /** + File Transfer Service (libftp) + */ + GS_CSP_PORT_FTP = 9, + /** + Remote log service (liblog) + */ + GS_CSP_PORT_RLOG = 11, + /** + Remote GOSH service (librgosh) + */ + GS_CSP_PORT_RGOSH = 12, + /** + AIS command port (libais). + */ + GS_CSP_PORT_AIS = 13, + /** + ADS-B command port (libadsb). + */ + GS_CSP_PORT_ADSB = 14, + + /** + GomSpace Sensor Bus (libgssb). + */ + GS_CSP_PORT_GSSB = 16, + /** + Flight Planner (libfp). + */ + GS_CSP_PORT_FP = 18, + /** + ADCS (libadcs). + */ + GS_CSP_PORT_ADCS = 20, + /** + House Keeping (libhk). + */ + GS_CSP_PORT_HK = 21, + /** + G(omSpace) script service (libgosh) + */ + GS_CSP_PORT_GSCRIPT = 22, + /** + Remote shell (libgosh). + Executes shell commands (linux server only). + Requires CSP_O_RDP. + */ + GS_CSP_PORT_REMOTE_SHELL = 27, + /** + House keeping beacon port (libhk). + Default port for sending beacons from satellite to ground (configurable). + */ + GS_CSP_PORT_HK_BEACON = 30, + +} gs_csp_port_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/router.h b/gomspace/libgscsp/include/gs/csp/router.h new file mode 100644 index 00000000..dae8990a --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/router.h @@ -0,0 +1,36 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_ROUTER_H +#define LIBGSCSP_INCLUDE_GS_CSP_ROUTER_H +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP router task, with support for stopping/terminating router task. +*/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Start CSP router (task/thread). + @param[in] stack_size stack size in bytes, minimum 300 bytes. + @param[in] priority task priority, normally GS_THREAD_PRIORITY_HIGH. + @return_gs_error_t +*/ +gs_error_t gs_csp_router_task_start(size_t stack_size, gs_thread_priority_t priority); + +/** + Stop CSP router task (for testing). + + Signal stop to the router and waits for it to terminate (join). + @note Join is performed, which may hang forever if the router doesn't respond to the stop request. + @return_gs_error_t +*/ +gs_error_t gs_csp_router_task_stop(void); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/rtable.h b/gomspace/libgscsp/include/gs/csp/rtable.h new file mode 100644 index 00000000..511b7bc2 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/rtable.h @@ -0,0 +1,31 @@ +#ifndef GS_CSP_RTABLE_H +#define GS_CSP_RTABLE_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP routing table support. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Load routing table. + Extension to csp_rtable_load(). + + @param[in] rtable string containing the routing table to set/load. + @param[in] set_default_route if \a true, sets a default route if no routing table specified and only one interface configured. + @param[in] use_command_line_option if \a true (and command line supported), use command line options to configure routing. + @return_gs_error_t +*/ +gs_error_t gs_csp_rtable_load(const char * rtable, bool set_default_route, bool use_command_line_option); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/service_dispatcher.h b/gomspace/libgscsp/include/gs/csp/service_dispatcher.h new file mode 100644 index 00000000..e80ef66d --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/service_dispatcher.h @@ -0,0 +1,124 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_SERVICE_DISPATCHER_H +#define LIBGSCSP_INCLUDE_GS_CSP_SERVICE_DISPATCHER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP Service Dispatcher. + + The dispatcher is a task/thread, listening on a configured number of ports and forwards the the incoming connection + to a configured CSP service handler. + The dispatcher touches a software watchdog for every handled connection. +*/ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + CSP service dispatcher configuration. +*/ +typedef struct { + /** + Name of dispatcher. + */ + const char * name; + /** + Array of service handlers - index'ed by CSP port number. + */ + const gs_csp_service_handler_t * handler_array; + /** + Array size of \a handler_array. + */ + unsigned int handler_array_size; + /** + Bind to any unbound ports (CSP_ANY). + Only one task/dispatcher can bind to any ports. This can be used to log unexpected incoming connections. + */ + bool bind_any; + /** + Disable watchdog. + The watchdog is created with a fixed timeout of 20 seconds. + */ + bool disable_watchdog; + /** + CSP connection backlog. + If 0 (zero), the backlog is set to 10. + */ + size_t listen_backlog; + /** + Callback after timeout or processsed connection. + The return value is the next timeout in milli seconds. + Return UINT32_MAX to use default timeout. + */ + unsigned int (*callback)(void); + /** + Socket options. + */ + uint32_t socket_options; +} gs_csp_service_dispatcher_conf_t; + +/** + Basic CSP service handlers. + + Can be used to configure handlers for all basic CSP services. +*/ +#define GS_CSP_BASIC_SERVICE_HANDLERS \ + [GS_CSP_CMP] = gs_csp_cmp_service_handler, \ + [GS_CSP_PING] = gs_csp_ping_service_handler, \ + [GS_CSP_PS] = gs_csp_ps_service_handler, \ + [GS_CSP_MEMFREE] = gs_csp_mem_free_service_handler, \ + [GS_CSP_REBOOT] = gs_csp_reboot_service_handler, \ + [GS_CSP_BUF_FREE] = gs_csp_buf_free_service_handler, \ + [GS_CSP_UPTIME] = gs_csp_uptime_service_handler + +/** + Service dispatcher handle. + @see gs_csp_service_dispatcher_create(), gs_csp_service_dispatcher_close() +*/ +typedef struct gs_csp_service_dispatcher * gs_csp_service_dispatcher_t; + +/** + Create service dispatcher (task/thread). + + @param[in] conf configuration - must remain valid as long as the dispatcher is running. + @param[in] stack_size thread stack size. + @param[in] priority thread priority. + @param[out] return_handle created dispatcher - use NULL if not used. + @return_gs_error_t +*/ +gs_error_t gs_csp_service_dispatcher_create(const gs_csp_service_dispatcher_conf_t * conf, + size_t stack_size, + gs_thread_priority_t priority, + gs_csp_service_dispatcher_t * return_handle); + +/** + Wake up service dispatcher. + + This will cause the disapther to wake up (from listing for new connections), and invoke the configured \a callback function, + before listing for new connections. + + @param[in] handle dispatcher. + @return_gs_error_t +*/ +gs_error_t gs_csp_service_dispatcher_wake_up(gs_csp_service_dispatcher_t handle); + +/** + Destroy/close service dispatcher (for testing). + + Signal stop to the dispatcher and waits for it to terminate (join). + + @note Join is performed, which may hang forever if the dispatcher doesn't respond to the stop request. + @param[in] handle dispatcher. + @return_gs_error_t +*/ +gs_error_t gs_csp_service_dispatcher_destroy(gs_csp_service_dispatcher_t handle); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/include/gs/csp/service_handler.h b/gomspace/libgscsp/include/gs/csp/service_handler.h new file mode 100644 index 00000000..52479129 --- /dev/null +++ b/gomspace/libgscsp/include/gs/csp/service_handler.h @@ -0,0 +1,102 @@ +#ifndef LIBGSCSP_INCLUDE_GS_CSP_SERVICE_HANDLER_H +#define LIBGSCSP_INCLUDE_GS_CSP_SERVICE_HANDLER_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + CSP Service handler. +*/ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + CSP Service handler. + + It is the handler's responsibility to process all pakcets on the connection and close the connection when done - even if a failure + is returned. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +typedef gs_error_t (*gs_csp_service_handler_t)(csp_conn_t * conn); + +/** + Service handler for CSP Management Protocol. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_cmp_service_handler(csp_conn_t * conn); + +/** + Service handler for ping. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_ping_service_handler(csp_conn_t * conn); + +/** + Service handler for getting process list. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_ps_service_handler(csp_conn_t * conn); + +/** + Service handler for getting memory free. + + Invokes GomSpace handler, which doesn't use malloc() to determine \a free memory. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_mem_free_service_handler(csp_conn_t * conn); + + +/** + Service handler for reboot (reset) of node. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_reboot_service_handler(csp_conn_t * conn); + +/** + Service handler for getting free CSP buffers. + + Invokes standard libcsp service handler. + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_buf_free_service_handler(csp_conn_t * conn); + +/** + Service handler for getting uptime (in seconds). + + Invokes GomSpace handler (works on Linux). + + @param[in] conn incoming connection. + @return_gs_error_t +*/ +gs_error_t gs_csp_uptime_service_handler(csp_conn_t * conn); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/lib/libcsp/CHANGELOG b/gomspace/libgscsp/lib/libcsp/CHANGELOG new file mode 100644 index 00000000..a4945716 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/CHANGELOG @@ -0,0 +1,113 @@ +libcsp 1.4, 07-05-2015 +---------------------- +- new: General rtable interface with support for STATIC or CIDR format +- new: CIDR (classless interdomain routing) route table format with netmasks +- new: Bridge capability +- new: Added routing table (de)serialization functions for load/save +- new: Automatic packet deduplication using CRC32 (compile time option) +- new: Autogenerated python bindings using ctypesgen +- new: Task-less operation with router invocation from external scheduler function +- api: Refactor route_if_add to csp_iflist_add +- api: Refactor route_set and friends to rtable_set +- api: Refactor csp_fifo_qos to csp_qfifo +- api: Added defined to be backwards compatible with 1.x +- interfaces: Drop packets on LOOP interface not for own address (blackhole) +- interfaces: New ZMQHUB interface (using zeroMQ over TCP) +- other: Increase stack size from 250 to 1100 for csp_can_rx_task +- other: Cleanup in csp_route.c +- other: Show incoming interface name in debug message +- other: Remove newlines from debug calls +- improvement: Reduce debug hook function complexity with valist passing +- fix: csp_sleep_ms did not work + +libcsp 1.3, 07-05-2015 +---------------------- +- new: Split long process lists into multiple packets +- new: Added posix csp_clock.h +- new: cmp clock functions (requires that you provide csp_clock.h implementation) +- new: Added SFP (Small fragmentation protocol) for larger data chunks +- fix: csp_if_fifo example +- fix: NULL char at the end of rps +- doc: Updated mtu documentation +- other: Tested with FreeRTOS 8.0 +- other: Added disable-stlib option to build only object files + +libcsp 1.2, 25-10-2013 +---------------------- +- Feature release +- New: CMP service for peek and poke of memory +- New: CMP interface statistics struct is now packed +- New: Faster O(1) buffer system with reference counting and automatic alignment +- New: Thread safe KISS driver with support for multiple interfaces +- New: CSP interface struct now holds an opaque pointer to driver handle +- New: removed TXBUF from KISS driver entirely to minimize stack usage, added TX lock instead +- New: Pre-calculated CRC table .romem or PROGMEM on __avr__ +- New: Added buffer overflow protection to KISS interface +- New: Allow posting null pointers on conn RX queues +- New: Lower memory usage on AVR8 +- New: csp_route_save and csp_route_load functions +- New: option --disable-verbose to disable filenames and linenumber on debug +- Protocol: KISS uses csp_crc32 instead of it own embedded crc32 +- Improvement: Use buffer clone function to copy promisc packets +- Bugfix: Fix pointer size (32/16bit) in cmp_peek/poke +- Bugfix: Issue with double free in KISS fixed +- Bugfix: Change rdp_send timeout from packet to connection timeout to make sending task block longer +- Bugfix: Fix conn pool leak when using security check and discarding new packets +- Bugfix: Add packet too short check for CRC32 +- Bugfix: Accept CRC32 responses from nodes without CRC support +- Bugfix: Ensure csp_ping works for packets > 256 bytes +- Bugfix: Cleanup printf inside ISR functions +- Bugfix: Do not add forwarded packets to promisc queue twice +- Bugfix: Fix return value bug of csp_buffer_get when out of buffers +- Bugfix: Always post null pointer with lowest priority, not highest +- Bugfix: Add check on debug level before calling do_csp_debug, fixes #35 +- Other: Export csp/arch include files +- Other: Remove the use of bool type from csp_debug +- Other: Moved csp debug functions to csp_debug.h instead of csp.h +- Other: Ensure assignment of id happens using the uint32_t .ext value of the union, quenches warning + +libcsp 1.1, 24-08-2012 +---------------------- +- Feature release +- Defacto stable since Feb 2012 +- New: I2C interface +- New: KISS interface +- New: USART drivers for Linux, Mac and Windows +- New: Windows/MinGW support +- New: MacOSX support +- New: Interface register function +- New: Interface search function +- New: CMP service for remote route updating +- New: CMP service for interface statistics +- Improvement: Better QoS support +- Improvement: Send RDP control messages with high priority +- Improvement: WAF distcheck now works +- Improvement: Automatic endian discovery +- Improvement: Accept packets with CRC32 checksum if compiled without CRC32 support +- Improvement: Do not wake the router task if RDP is not enabled +- Improvement: Save 102 bytes of RAM by packing route entries +- Cleanup: Simplify CAN configuration +- Cleanup: Move architecture specific code to src/arch +- Bugfix: CSP_MEMFREE gives wrong answer on freertos AVR due to truncation +- Bugfix: Fixed wrong 64-bit size_t in csp_service_handler +- Bugfix: Fixed problem in csp_if_kiss when out of buffers +- Bigfix: Handle bus-off CAN IRQ for AT90CAN128 + +libcsp 1.0.1, 30-10-2011 +------------------------ +- Hotfix release +- Bugfix: missing extern in csp_if_lo.h + +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: Connection-oriented transport protocol w. flow-control +- Features: Connection-less "UDP" like transport +- Features: Encryption, Authentication and message check +- Features: Loopback interface +- Features: Python Bindings +- Features: CAN interface w. drivers for several chips +- Features: CSP-services (ping, reboot, uptime, memfree, buffree, ident) + diff --git a/gomspace/libgscsp/lib/libcsp/CONTRIBUTORS b/gomspace/libgscsp/lib/libcsp/CONTRIBUTORS new file mode 100644 index 00000000..97240f60 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/CONTRIBUTORS @@ -0,0 +1,3 @@ +Jeppe Ledet-Pedersen +Johan De Claville Christiansen +Dan Erik Holmstrøm diff --git a/gomspace/libgscsp/lib/libcsp/COPYING b/gomspace/libgscsp/lib/libcsp/COPYING new file mode 100644 index 00000000..54c619ad --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/COPYING @@ -0,0 +1,503 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/gomspace/libgscsp/lib/libcsp/INSTALL.rst b/gomspace/libgscsp/lib/libcsp/INSTALL.rst new file mode 100644 index 00000000..e68a46ed --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/INSTALL.rst @@ -0,0 +1,30 @@ +How to install LibCSP +===================== + +CSP uses the `waf` build system (http://code.google.com/p/waf/). In order to +compile CSP, you first need to configure the toolchain, what operating system +to compile for, the location of required libraries and whether to enable +certain optional features. + +To configure CSP to build with the AVR32 toolchain for FreeRTOS and output +the compiled libcsp.a and header files to the install directory, issue: + +.. code-block:: bash + + ./waf configure --toolchain=avr32- --with-os=freertos --prefix=install + +When compiling for FreeRTOS, the path to the FreeRTOS header files must be +specified with `--with-freertos=PATH.` + +A number of optional features can be enabled by from the configure script. +Support for XTEA encryption can e.g. be enabled with `--enable-xtea`. Run +`./waf configure --help` to list the available configure options. + +The CAN drivers can be enabled by appending the configure option `--with-driver-can=CHIP`, +where CHIP is one of 'socketcan', 'at91sam7a1', 'at91sam7a3' or 'at90can128'. + +To build and copy the library to the location specified with --prefix, use: + +.. code-block:: bash + + ./waf build install diff --git a/gomspace/libgscsp/lib/libcsp/README.rst b/gomspace/libgscsp/lib/libcsp/README.rst new file mode 100644 index 00000000..c8aff3d8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/README.rst @@ -0,0 +1,41 @@ +The Cubesat Space Protocol +========================== + +Cubesat Space Protocol (CSP) is a small protocol stack written in C. CSP is designed to ease communication between distributed embedded systems in smaller networks, such as Cubesats. The design follows the TCP/IP model and includes a transport protocol, a routing protocol and several MAC-layer interfaces. The core of libcsp includes a router, a socket buffer pool and a connection oriented socket API. + +The protocol is based on a 32-bit header containing both transport and network-layer information. Its implementation is designed for, but not limited to, embedded systems such as the 8-bit AVR microprocessor and the 32-bit ARM and AVR from Atmel. The implementation is written in GNU C and is currently ported to run on FreeRTOS or POSIX operating systems such as Linux. + +The idea is to give sub-system developers of cubesats the same features of a TCP/IP stack, but without adding the huge overhead of the IP header. The small footprint and simple implementation allows a small 8-bit system with less than 4 kB of RAM 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 on many cubesats. + + * Standardised network protocol: All subsystems can communicate with eachother + * Service loose coupling: Services maintain a relationship that minimizes dependencies between subsystems + * Service abstraction: Beyond descriptions in the service contract, services hide logic from the outside world + * Service reusability: Logic is divided into services with the intention of promoting reuse. + * Service autonomy: Services have control over the logic they encapsulate. + * Service Redundancy: Easily add redundant services to the bus + * Reduces single point of failure: The complexity is moved from a single master node to several well defines services on the network + +The implementation of LibCSP is written with simplicity in mind, but it's compile time configuration allows it to have some rather advanced features as well: + +Features +-------- + + * Thread safe Socket API + * Router task with Quality of Services + * Connection-oriented operation (RFC 908 and 1151). + * Connection-less operation (similar to UDP) + * ICMP-like requests such as ping and buffer status. + * Loopback interface + * Very Small Footprint 48 kB code and less that 1kB ram required on ARM + * Zero-copy buffer and queue system + * Modular network interface system + * Modular OS interface, ported to FreeRTOS, windows (cygwin) and Linux + * Broadcast traffic + * Promiscuous mode + * Encrypted packets with XTEA in CTR mode + * Truncated HMAC-SHA1 Authentication (RFC 2104) + +LGPL Software license +--------------------- +The source code is available under an LGPL 2.1 license. See COPYING for the license text. + diff --git a/gomspace/libgscsp/lib/libcsp/bindings/python/libcsp/__init__.py b/gomspace/libgscsp/lib/libcsp/bindings/python/libcsp/__init__.py new file mode 100644 index 00000000..39de36b5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/bindings/python/libcsp/__init__.py @@ -0,0 +1,6 @@ +import sys + +if sys.version_info >= (3, 0): + from libcsp_py3 import * +else: + from libcsp_py2 import * diff --git a/gomspace/libgscsp/lib/libcsp/doc/example.rst b/gomspace/libgscsp/lib/libcsp/doc/example.rst new file mode 100644 index 00000000..b82a055e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/example.rst @@ -0,0 +1,123 @@ +Client and server example +========================= + +The following examples show the initialization of the protocol stack and examples of client/server code. + +Initialization Sequence +----------------------- + +This code initializes the CSP buffer system, device drivers and router core. The example uses the CAN interface function csp_can_tx but the initialization is similar for other interfaces. The loopback interface does not require any explicit initialization. + +.. code-block:: c + + #include + #include + + /* CAN configuration struct for SocketCAN interface "can0" */ + struct csp_can_config can_conf = {.ifc = "can0"}; + + /* Init buffer system with 10 packets of maximum 320 bytes each */ + csp_buffer_init(10, 320); + + /* Init CSP with address 1 */ + csp_init(1); + + /* Init the CAN interface with hardware filtering */ + csp_can_init(CSP_CAN_MASKED, &can_conf) + + /* Setup default route to CAN interface */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_can_tx, CSP_HOST_MAC); + + /* Start router task with 500 word stack, OS task priority 1 */ + csp_route_start_task(500, 1); + +Server +------ + +This example shows how to create a server task that listens for incoming connections. CSP should be initialized before starting this task. Note the use of `csp_service_handler()` as the default branch in the port switch case. The service handler will automatically reply to ICMP-like requests, such as pings and buffer status requests. + +.. code-block:: c + + void csp_task(void *parameters) { + /* Create socket without any socket options */ + csp_socket_t *sock = csp_socket(CSP_SO_NONE); + + /* Bind all ports to socket */ + csp_bind(sock, CSP_ANY); + + /* Create 10 connections backlog queue */ + csp_listen(sock, 10); + + /* Pointer to current connection and packet */ + csp_conn_t *conn; + csp_packet_t *packet; + + /* Process incoming connections */ + while (1) { + /* Wait for connection, 10000 ms timeout */ + if ((conn = csp_accept(sock, 10000)) == NULL) + continue; + + /* Read packets. Timout is 1000 ms */ + while ((packet = csp_read(conn, 1000)) != NULL) { + switch (csp_conn_dport(conn)) { + case MY_PORT: + /* Process packet here */ + default: + /* Let the service handler reply pings, buffer use, etc. */ + csp_service_handler(conn, packet); + break; + } + } + + /* Close current connection, and handle next */ + csp_close(conn); + } + } + +Client +------ + +This example shows how to allocate a packet buffer, connect to another host and send the packet. CSP should be initialized before calling this function. RDP, XTEA, HMAC and CRC checksums can be enabled per connection, by setting the connection option to a bitwise OR of any combination of `CSP_O_RDP`, `CSP_O_XTEA`, `CSP_O_HMAC` and `CSP_O_CRC`. + +.. code-block:: c + + int send_packet(void) { + + /* Get packet buffer for data */ + csp_packet_t *packet = csp_buffer_get(data_size); + if (packet == NULL) { + /* Could not get buffer element */ + printf("Failed to get buffer element\\n"); + return -1; + } + + /* Connect to host HOST, port PORT with regular UDP-like protocol and 1000 ms timeout */ + csp_conn_t *conn = csp_connect(CSP_PRIO_NORM, HOST, PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + /* Connect failed */ + printf("Connection failed\\n"); + /* Remember to free packet buffer */ + csp_buffer_free(packet); + return -1; + } + + /* Copy message to packet */ + char *msg = "HELLO"; + strcpy(packet->data, msg); + + /* Set packet length */ + packet->length = strlen(msg); + + /* Send packet */ + if (!csp_send(conn, packet, 1000)) { + /* Send failed */ + printf("Send failed\\n"); + csp_buffer_free(packet); + } + + /* Close connection */ + csp_close(conn); + + return 0 + } diff --git a/gomspace/libgscsp/lib/libcsp/doc/history.rst b/gomspace/libgscsp/lib/libcsp/doc/history.rst new file mode 100644 index 00000000..ad064873 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/history.rst @@ -0,0 +1,17 @@ +History +======= + +The idea was developed by a group of students from Aalborg University in 2008. In 2009 the main developer started working for GomSpace, and CSP became integrated into the GomSpace products. The protocol is based on a 32-bit header containing both transport, network and MAC-layer information. It's implementation is designed for, but not limited to, embedded systems such as the 8-bit AVR microprocessor and the 32-bit ARM and AVR from Atmel. The implementation is written in C and is currently ported to run on FreeRTOS and POSIX and pthreads based operating systems like Linux and BSD. The three letter acronym CSP was originally an abbreviation for CAN Space Protocol because the first MAC-layer driver was written for CAN-bus. Now the physical layer has extended to include spacelink, I2C and RS232, the name was therefore extended to the more general CubeSat Space Protocol without changing the abbreviation. + +Satellites using CSP +-------------------- + +This is the known list of satellites or organisations that uses CSP. + + * GomSpace GATOSS GOMX-1 + * AAUSAT-3 + * EgyCubeSat + * EuroLuna + * NUTS + * Hawaiian Space Flight Laboratory + * GomSpace GOMX-3 diff --git a/gomspace/libgscsp/lib/libcsp/doc/interfaces.rst b/gomspace/libgscsp/lib/libcsp/doc/interfaces.rst new file mode 100644 index 00000000..5a80325c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/interfaces.rst @@ -0,0 +1,95 @@ +CSP Interfaces +============== + +This is an example of how to implement a new layer-2 interface in CSP. The example is going to show how to create a `csp_if_fifo`, using a set of [named pipes](http://en.wikipedia.org/wiki/Named_pipe). The complete interface example code can be found in `examples/fifo.c`. For an example of a fragmenting interface, see the CAN interface in `src/interfaces/csp_if_can.c`. + +CSP interfaces are declared in a `csp_iface_t` structure, which sets the interface nexthop function and name. A maximum transmission unit can also be set, which forces CSP to drop outgoing packets above a certain size. The fifo interface is defined as: + +.. code-block:: c + + #include + #include + + csp_iface_t csp_if_fifo = { + .name = "fifo", + .nexthop = csp_fifo_tx, + .mtu = BUF_SIZE, + }; + +Outgoing traffic +---------------- + +The nexthop function takes a pointer to a CSP packet and a timeout as parameters. All outgoing packets that are routed to the interface are passed to this function: + +.. code-block:: c + + int csp_fifo_tx(csp_packet_t *packet, uint32_t timeout) { + write(tx_channel, &packet->length, packet->length + sizeof(uint32_t) + sizeof(uint16_t)); + csp_buffer_free(packet); + return 1; + } + +In the fifo interface, we simply transmit the header, length field and data using a write to the fifo. CSP does not dictate the wire format, so other interfaces may decide to e.g. ignore the length field if the physical layer provides start/stop flags. + +_Important notice: If the transmission succeeds, the interface must free the packet and return 1. If transmission fails, the nexthop function should return 0 and not free the packet, to allow retransmissions by the caller._ + +Incoming traffic +---------------- + +The interface also needs to receive incoming packets and pass it to the CSP protocol stack. In the fifo interface, this is handled by a thread that blocks on the incoming fifo and waits for packets: + +.. code-block:: c + + void * fifo_rx(void * parameters) { + csp_packet_t *buf = csp_buffer_get(BUF_SIZE); + /* Wait for packet on fifo */ + while (read(rx_channel, &buf->length, BUF_SIZE) > 0) { + csp_qfifo_write(buf, &csp_if_fifo, NULL); + buf = csp_buffer_get(BUF_SIZE); + } + } + +A new CSP buffer is preallocated with csp_buffer_get(). When data is received, the packet is passed to CSP using `csp_qfifo_write()` and a new buffer is allocated for the next packet. In addition to the received packet, `csp_qfifo_write()` takes two additional arguments: + +.. code-block:: c + + void csp_qfifo_write(csp_packet_t *packet, csp_iface_t *interface, CSP_BASE_TYPE *pxTaskWoken); + +The calling interface must be passed in `interface` to avoid routing loops. Furthermore, `pxTaskWoken` must be set to a non-NULL value if the packet is received in an interrupt service routine. If the packet is received in task context, NULL must be passed. 'pxTaskWoken' only applies to FreeRTOS systems, and POSIX system should always set the value to NULL. + +`csp_qfifo_write` will either accept the packet or free the packet buffer, so the interface must never free the packet after passing it to CSP. + +Initialization +-------------- + +In order to initialize the interface, and make it available to the router, use the following function found in `csp/csp_interface.h`: + +.. code-block:: c + + csp_route_add_if(&csp_if_fifo); + +This actually happens automatically if you try to call `csp_route_add()` with an interface that is unknown to the router. This may however be removed in the future, in order to ensure that all interfaces are initialised before configuring the routing table. The reason is, that some products released in the future may ship with an empty routing table, which is then configured by a routing protocol rather than a static configuration. + +In order to setup a manual static route, use the following example where the default route is set to the fifo interface: + +.. code-block:: c + + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_fifo, CSP_NODE_MAC); + +All outgoing traffic except loopback, is now passed to the fifo interface's nexthop function. + +Building the example +-------------------- + +The fifo examples can be compiled with: + +.. code-block:: bash + + % gcc csp_if_fifo.c -o csp_if_fifo -I/include -L/build -lcsp -lpthread -lrt + +The two named pipes are created with: + +.. code-block:: bash + + % mkfifo server_to_client client_to_server + diff --git a/gomspace/libgscsp/lib/libcsp/doc/libcsp.rst b/gomspace/libgscsp/lib/libcsp/doc/libcsp.rst new file mode 100644 index 00000000..f866015f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/libcsp.rst @@ -0,0 +1,21 @@ +.. CSP Documentation master file. + +.. _libcsp: + +********************** +CubeSat Space Protocol +********************** + +.. toctree:: + :maxdepth: 3 + + ../README + history + structure + interfaces + memory + protocolstack + topology + mtu + example + diff --git a/gomspace/libgscsp/lib/libcsp/doc/memory.rst b/gomspace/libgscsp/lib/libcsp/doc/memory.rst new file mode 100644 index 00000000..4e38d711 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/memory.rst @@ -0,0 +1,28 @@ +How CSP uses memory +=================== + +CSP has been written for small microprocessor systems. The way memory is handled is therefore a tradeoff between the amount used and the code efficiency. This section tries to give some answers to what the memory is used for and how it it used. The primary memory blocks in use by CSP is: + + * Routing table + * Ports table + * Connection table + * Buffer pool + * Interface list + +Tables +------ +The reason for using tables for the routes, ports and connections is speed. When a new packet arrives the core of CSP needs to do a quick lookup in the connection so see if it can find an existing connection to which the packet matches. If this is not found, it will take a lookup in the ports table to see if there are any applications listening on the incoming port number. Another argument of using tables are pre-allocation. The linker will reserve an area of the memory for which the routes and connections can be stored. This avoid an expensive `malloc()` call during initialization of CSP, and practically costs zero CPU instructions. The downside of using tables are the wasted memory used by unallocated ports and connections. For the routing table the argumentation is the same, pre-allocation is better than calling `malloc()`. + +Buffer Pool +----------- + +The buffer handling system can be compiled for either static allocation or a one-time dynamic allocation of the main memory block. After this, the buffer system is entirely self-contained. All allocated elements are of the same size, so the buffer size must be chosen to be able to handle the maximum possible packet length. The buffer pool uses a queue to store pointers to free buffer elements. First of all, this gives a very quick method to get the next free element since the dequeue is an O(1) operation. Furthermore, since the queue is a protected operating system primitive, it can be accessed from both task-context and interrupt-context. The `csp_buffer_get` version is for task-context and `csp_buffer_get_isr` is for interrupt-context. Using fixed size buffer elements that are preallocated is again a question of speed and safety. + + +A basic concept of the buffer system is called Zero-Copy. This means that from userspace to the kernel-driver, the buffer is never copied from one buffer to another. This is a big deal for a small microprocessor, where a call to `memcpy()` can be very expensive. In practice when data is inserted into a packet, it is shifted a certain number of bytes in order to allow for a packet header to be prepended at the lower layers. This also means that there is a strict contract between the layers, which data can be modified and where. The buffer object is normally casted to a `csp_packet_t`, but when its given to an interface on the MAC layer it's casted to a `csp_i2c_frame_t` for example. + +Interface list +-------------- + +The interface list is a simple single-ended linked list of references to the interface specification structures. These structures are static const and allocated by the linker. The pointer to this data is inserted into the list one time during setup of the interface. Each entry in the routing table has a direct pointer to the interface element, thereby avoiding list lookup, but the list is needed in order for the dynamic route configuration to know which interfaces are available. + diff --git a/gomspace/libgscsp/lib/libcsp/doc/mtu.rst b/gomspace/libgscsp/lib/libcsp/doc/mtu.rst new file mode 100644 index 00000000..27753300 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/mtu.rst @@ -0,0 +1,19 @@ +Maximum Transfer Unit +===================== + +There are two things limiting the MTU of CSP. + + 1. The pre-allocated buffer pool’s allocation size + 2. The link layer protocol. + +So let’s assume that you have made a protocol called KISS with a MTU of 256. The 256 is the total amount of data that you can put into the CSP-packet. However, you need to take the overhead of the link layer into account. Typically this could consist of a length field and/or a start/stop flag. So the actual frame size on the link layer would for example be 256 bytes of data + 2 bytes sync flag + 2 bytes length field. + +This requires a buffer allocation of at lest 256 + 2 + 2. However, the CSP packet itself has some reserved bytes in the beginning of the packet (which you can see in csp.h) - so the recommended buffer allocation size is MAX MTU + 16 bytes. In this case the max MTU would be 256. + +If you try to pass data which is longer than the MTU, the chance is that you will also make a buffer overflow in the CSP buffer pool. However, lets assume that you have two interfaces one with an MTU of 200 bytes and another with an MTU of 100 bytes. In this case you might successfully transfer 150 bytes over the first interface, but the packet will be rejected once it comes to the second interface. + +If you want to increase your MTU of a specific link layer, it is up to the link layer protocol to implement its own fragmentation protocol. A good example is CAN-bus which only allows a frame size of 8 bytes. libcsp have a small protocol for this called the “CAN fragmentation protocol" or CFP for short. This allows data of much larger size to be transferred over the CAN bus. + +Okay, but what if you want to transfer 1000 bytes, and the network maximum MTU is 256? Well, since CSP does not include streaming sockets, only packet’s. Somebody will have to split that data up into chunks. It might be that you application have special knowledge about the datatype you are transmitting, and that it makes sense to split the 1000 byte content into 10 chunks of 100 byte status messages. This, application layer delimitation might be good if you have a situation with packet loss, because your receiver could still make good usage of the partially delivered chunks. + +But, what if you just want 1000 bytes transmitted, and you don’t care about the fragmentation unit, and also don’t want the hassle of writing the fragmentation code yourself? - In this case, libcsp now features a new (still experimental) feature called SFP (small fragmentation protocol) designed to work on the application layer. For this purpose you will not use csp_send and csp_recv, but csp_sfp_send and csp_sfp_recv. This will split your data into chunks of a certain size, enumerate them and transfer over a given connection. If a chunk is missing the SFP client will abort the reception, because SFP does not provide retransmission. If you wish to also have retransmission and orderly delivery you will have to open an RDP connection and send your SFP message to that connection. diff --git a/gomspace/libgscsp/lib/libcsp/doc/protocolstack.rst b/gomspace/libgscsp/lib/libcsp/doc/protocolstack.rst new file mode 100644 index 00000000..365aabbe --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/protocolstack.rst @@ -0,0 +1,54 @@ +The Protocol Stack +================== + +The CSP protocol stack includes functionality on all layers of the TCP/IP model: + +Layer 1: Drivers +---------------- + +Lib CSP is not designed for any specific processor or hardware peripheral, but yet these drivers are required in order to work. The intention of LibCSP is not to provide CAN, I2C or UART drivers for all platforms, however some drivers has been included for some platforms. If you do not find your platform supported, it is quite simple to add a driver that conforms to the CSP interfaces. For example the I2C driver just requires three functions: `init`, `send` and `recv`. For good stability and performance interrupt driven drivers are preferred in favor of polled drivers. Where applicable also DMA usage is recommended. + +Layer 2: MAC interfaces +----------------------- + +CSP has interfaces for I2C, CAN, RS232 (KISS) and Loopback. The layer 2 protocol software defines a frame-format that is suitable for the media. CSP can be easily extended with implementations for even more links. For example a radio-link and IP-networks. The file `csp_interface.h` declares the rx and tx functions needed in order to define a network interface in CSP. During initialisation of CSP each interface will be inserted into a linked list of interfaces that is available to the router. In cases where link-layer addresses are required, such as I2C, the routing table supports specifying next-hop link-layer address directly. This avoids the need to implement an address resolution protocol to translate CSP addresses to I2C addresses. + +Layer 3: Network Router +----------------------- + +The router core is the backbone of the CSP implementation. The router works by looking at a 32-bit CSP header which contains the delivery and source address together with port numbers for the connection. Each router supports both local delivery and forwarding of frames to another destination. Frames will never exit the router on the same interface that they arrives at, this concept is called split horizon, and helps prevent routing loops. + +The main purpose of the router is to accept incoming packets and deliver them to the right message queue. Therefore, in order to listen on a port-number on the network, a task must create a socket and call the accept() call. This will make the task block and wait for incoming traffic, just like a web-server or similar. When an incoming connection is opened, the task is woken. Depending on the task-priority, the task can even preempt another task and start execution immediately. + +There is no routing protocol for automatic route discovery, all routing tables are pre-programmed into the subsystems. The table itself contains a separate route to each of the possible 32 nodes in the network and the additional default route. This means that the overall topology must be decided before putting sub-systems together, as explained in the `topology.md` file. However CSP has an extension on port zero CMP (CSP management protocol), which allows for over-the-network routing table configuration. This has the advantage that default routes could be changed if for example the primary radio fails, and the secondary should be used instead. + +Layer 4: Transport Layer +------------------------ + +LibCSP implements two different Transport Layer protocols, they are called UDP (unreliable datagram protocol) and RDP (reliable datagram protocol). The name UDP has not been chosen to be an exact replica of the UDP (user datagram protocol) known from the TCP/IP model, but they have certain similarities. + +The most important thing to notice is that CSP is entirely a datagram service. There is no stream based service like TCP. A datagram is defined a block of data with a specified size and structure. This block enters the transport layer as a single datagram and exits the transport layer in the other end as a single datagram. CSP preserves this structure all the way to the physical layer for I2C, KISS and Loopback interfaces are used. The CAN-bus interface has to fragment the datagram into CAN-frames of 8 bytes, however only a fully completed datagram will arrive at the receiver. + +UDP +^^^ + +UDP uses a simple transmission model without implicit hand-shaking dialogues for guaranteeing reliability, ordering, or data integrity. Thus, UDP provides an unreliable service and datagrams may arrive out of order, appear duplicated, or go missing without notice. UDP assumes that error checking and correction is either not necessary or performed in the application, avoiding the overhead of such processing at the network interface level. Time-sensitive applications often use UDP because dropping packets is preferable to waiting for delayed packets, which may not be an option in a real-time system. + +UDP is very practical to implement request/reply based communication where a single packet forms the request and a single packet forms the reply. In this case a typical request and wait protocol is used between the client and server, which will simply return an error if a reply is not received within a specified time limit. An error would normally lead to a retransmission of the request from the user or operator which sent the request. + +While UDP is very simple, it also has some limitations. Normally a human in the loop is a good thing when operating the satellite over UDP. But when it comes to larger file transfers, the human becomes the bottleneck. When a high-speed file transfer is initiated data acknowledgment should be done automatically in order to speed up the transfer. This is where the RDP protocol can help. + +RDP +^^^ +CSP provides a transport layer extension called RDP (reliable datagram protocol) which is an implementation of RFC908 and RFC1151. RDP provides a few additional features: + + * Three-way handshake + * Flow Control + * Data-buffering + * Packet re-ordering + * Retransmission + * Windowing + * Extended Acknowledgment + +For more information on this, please refer to RFC908. + diff --git a/gomspace/libgscsp/lib/libcsp/doc/structure.rst b/gomspace/libgscsp/lib/libcsp/doc/structure.rst new file mode 100644 index 00000000..4c9b515c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/structure.rst @@ -0,0 +1,27 @@ +Structure +========= +The Cubesat Space Protocol library is structured as shown in the following table: + +============================= ========================================================================= +**Folder** **Description** +============================= ========================================================================= +libcsp/include/csp Main include files +libcsp/include/csp/arch Architecture include files +libcsp/include/csp/interfaces Interface include files +libcsp/include/csp/drivers Drivers include files +libcsp/src Main modules for CSP: io, router, connections, services +libcsp/src/interfaces Interface modules for CAN, I2C, KISS, LOOP and ZMQHUB +libcsp/src/drivers/can Driver for CAN +libcsp/src/drivers/usart Driver for USART +libcsp/src/arch/freertos FreeRTOS architecture module +libcsp/src/arch/macosx Mac OS X architecture module +libcsp/src/arch/posix Posix architecture module +libcsp/src/arch/windows Windows architecture module +libcsp/src/rtable Routing table module +libcsp/transport Transport module, UDP and RDP +libcsp/crypto Crypto module +libcsp/utils Utilities +libcsp/bindings/python Python wrapper for libcsp +libcsp/examples CSP examples (source code) +libasf/doc The doc folder contains the source code for this documentation +============================= ========================================================================= diff --git a/gomspace/libgscsp/lib/libcsp/doc/topology.rst b/gomspace/libgscsp/lib/libcsp/doc/topology.rst new file mode 100644 index 00000000..e629c29e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/doc/topology.rst @@ -0,0 +1,26 @@ +Network Topology +================ + +CSP uses a network oriented terminology similar to what is known from the Internet and the TCP/IP model. A CSP network can be configured for several different topologies. The most common topology is to create two segments, one for the Satellite and one for the Ground-Station. + +.. code-block:: none + + I2C BUS + _______________________________ + / | | | \ + +---+ +---+ +---+ +---+ +---+ + |OBC| |COM| |EPS| |PL1| |PL2| Nodes 0 - 7 (Space segment) + +---+ +---+ +---+ +---+ +---+ + ^ + | Radio + v + +---+ +----+ + |TNC| ------- | PC | Nodes 8 - 15 (Ground segment) + +---+ USB +----+ + + Node 9 Node 10 + +The address range, from 0 to 15, has been segmented into two equal size segments. This allows for easy routing in the network. All addresses starting with binary 1 is on the ground-segment, and all addresses starting with 0 is on the space segment. From CSP v1.0 the address space has been increased to 32 addresses, 0 to 31. But for legacy purposes, the old 0 to 15 is still used in most products. + +The network is configured using static routes initialised at boot-up of each sub-system. This means that the basic routing table must be assigned compile-time of each subsystem. However each node supports assigning an individual route to every single node in the network and can be changed run-time. This means that the network topology can be easily reconfigured after startup. + diff --git a/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo.c b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo.c new file mode 100644 index 00000000..136fc3aa --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo.c @@ -0,0 +1,165 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TYPE_SERVER 1 +#define TYPE_CLIENT 2 +#define PORT 10 +#define BUF_SIZE 250 + +pthread_t rx_thread; +int rx_channel, tx_channel; + +int csp_fifo_tx(csp_iface_t *ifc, csp_packet_t *packet, uint32_t timeout); + +csp_iface_t csp_if_fifo = { + .name = "fifo", + .nexthop = csp_fifo_tx, + .mtu = BUF_SIZE, +}; + +int csp_fifo_tx(csp_iface_t *ifc, csp_packet_t *packet, uint32_t timeout) { + /* Write packet to fifo */ + if (write(tx_channel, &packet->length, packet->length + sizeof(uint32_t) + sizeof(uint16_t)) < 0) + printf("Failed to write frame\r\n"); + csp_buffer_free(packet); + return CSP_ERR_NONE; +} + +void * fifo_rx(void * parameters) { + csp_packet_t *buf = csp_buffer_get(BUF_SIZE); + /* Wait for packet on fifo */ + while (read(rx_channel, &buf->length, BUF_SIZE) > 0) { + csp_qfifo_write(buf, &csp_if_fifo, NULL); + buf = csp_buffer_get(BUF_SIZE); + } + + return NULL; +} + +int main(int argc, char **argv) { + + int me, other, type; + const char *message = "Testing CSP"; + const char *rx_channel_name; + const char *tx_channel_name; + csp_socket_t *sock; + csp_conn_t *conn; + csp_packet_t *packet; + + /* Run as either server or client */ + if (argc != 2) { + printf("usage: %s \r\n", argv[0]); + return -1; + } + + /* Set type */ + if (strcmp(argv[1], "server") == 0) { + me = 1; + other = 2; + tx_channel_name = "server_to_client"; + rx_channel_name = "client_to_server"; + type = TYPE_SERVER; + } else if (strcmp(argv[1], "client") == 0) { + me = 2; + other = 1; + tx_channel_name = "client_to_server"; + rx_channel_name = "server_to_client"; + type = TYPE_CLIENT; + } else { + printf("Invalid type. Must be either 'server' or 'client'\r\n"); + return -1; + } + + /* Init CSP and CSP buffer system */ + if (csp_init(me) != CSP_ERR_NONE || csp_buffer_init(10, 300) != CSP_ERR_NONE) { + printf("Failed to init CSP\r\n"); + return -1; + } + + tx_channel = open(tx_channel_name, O_RDWR); + if (tx_channel < 0) { + printf("Failed to open TX channel\r\n"); + return -1; + } + + rx_channel = open(rx_channel_name, O_RDWR); + if (rx_channel < 0) { + printf("Failed to open RX channel\r\n"); + return -1; + } + + /* Start fifo RX task */ + pthread_create(&rx_thread, NULL, fifo_rx, NULL); + + /* Set default route and start router */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_fifo, CSP_NODE_MAC); + csp_route_start_task(0, 0); + + /* Create socket and listen for incoming connections */ + if (type == TYPE_SERVER) { + sock = csp_socket(CSP_SO_NONE); + csp_bind(sock, PORT); + csp_listen(sock, 5); + } + + /* Super loop */ + while (1) { + if (type == TYPE_SERVER) { + /* Process incoming packet */ + conn = csp_accept(sock, 1000); + if (conn) { + packet = csp_read(conn, 0); + if (packet) + printf("Received: %s\r\n", packet->data); + csp_buffer_free(packet); + csp_close(conn); + } + } else { + /* Send a new packet */ + packet = csp_buffer_get(strlen(message)); + if (packet) { + strcpy((char *) packet->data, message); + packet->length = strlen(message); + + conn = csp_connect(CSP_PRIO_NORM, other, PORT, 1000, CSP_O_NONE); + printf("Sending: %s\r\n", message); + if (!conn || !csp_send(conn, packet, 1000)) + return -1; + csp_close(conn); + } + sleep(1); + } + } + + close(rx_channel); + close(tx_channel); + + return 0; +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo_windows.c b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo_windows.c new file mode 100644 index 00000000..5b360709 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/csp_if_fifo_windows.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#undef interface + +#include +#include + +#define PIPE_BUFSIZE 1024 + +#define TYPE_SERVER 1 +#define TYPE_CLIENT 2 +#define PORT 10 +#define BUF_SIZE 250 + + +static LPCTSTR pipeName = TEXT("\\\\.\\pipe\\CSP_Pipe"); + +static HANDLE pipe = INVALID_HANDLE_VALUE; + +unsigned WINAPI fifo_rx(void *); +unsigned WINAPI pipe_listener(void *); + +void printError(void); + +int csp_fifo_tx(csp_packet_t *packet, uint32_t timeout); + +csp_iface_t csp_if_fifo = { + .name = "fifo", + .nexthop = csp_fifo_tx, + .mtu = BUF_SIZE, +}; + +int csp_fifo_tx(csp_packet_t *packet, uint32_t timeout) { + printf("csp_fifo_tx tid: %lu\n", GetCurrentThreadId()); + DWORD expectedSent = packet->length + sizeof(uint32_t) + sizeof(uint16_t); + DWORD actualSent; + /* Write packet to fifo */ + if( !WriteFile(pipe, &packet->length, expectedSent, &actualSent, NULL) + || actualSent != expectedSent ) { + printError(); + } + + csp_buffer_free(packet); + return CSP_ERR_NONE; +} + + +int main(int argc, char *argv[]) { + int me, other, type; + char *message = "Testing CSP"; + csp_socket_t *sock = NULL; + csp_conn_t *conn = NULL; + csp_packet_t *packet = NULL; + + /* Run as either server or client */ + if (argc != 2) { + printf("usage: server \r\n"); + return -1; + } + + /* Set type */ + if (strcmp(argv[1], "server") == 0) { + me = 1; + other = 2; + type = TYPE_SERVER; + } else if (strcmp(argv[1], "client") == 0) { + me = 2; + other = 1; + type = TYPE_CLIENT; + } else { + printf("Invalid type. Must be either 'server' or 'client'\r\n"); + return -1; + } + + /* Init CSP and CSP buffer system */ + if (csp_init(me) != CSP_ERR_NONE || csp_buffer_init(10, 300) != CSP_ERR_NONE) { + printf("Failed to init CSP\r\n"); + return -1; + } + + if( type == TYPE_SERVER ) { + _beginthreadex(NULL, 0, pipe_listener, NULL, 0, 0); + } else { + pipe = CreateFile( + pipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + if( pipe == INVALID_HANDLE_VALUE ) { + printError(); + return -1; + } + } + + /* Set default route and start router */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_fifo, CSP_NODE_MAC); + csp_route_start_task(0, 0); + + /* Create socket and listen for incoming connections */ + if (type == TYPE_SERVER) { + sock = csp_socket(CSP_SO_NONE); + csp_bind(sock, PORT); + csp_listen(sock, 5); + } + + /* Super loop */ + while (1) { + if (type == TYPE_SERVER) { + /* Process incoming packet */ + conn = csp_accept(sock, 1000); + if (conn) { + packet = csp_read(conn, 0); + if (packet) + printf("Received: %s\r\n", packet->data); + csp_buffer_free(packet); + csp_close(conn); + } + } else { + /* Send a new packet */ + packet = csp_buffer_get(strlen(message)); + if (packet) { + strcpy((char *) packet->data, message); + packet->length = strlen(message); + + conn = csp_connect(CSP_PRIO_NORM, other, PORT, 1000, CSP_O_NONE); + printf("Sending: %s\r\n", message); + if (!conn || !csp_send(conn, packet, 1000)) + return -1; + csp_close(conn); + Sleep(1000); + } + } + } + + return 0; +} + +void printError(void) { + LPTSTR messageBuffer = NULL; + DWORD errorCode = GetLastError(); + DWORD formatMessageRet; + formatMessageRet = FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&messageBuffer, + 0, + NULL); + + if( !formatMessageRet ) { + wprintf(L"FormatMessage error, code: %lu\n", GetLastError()); + return; + } + + printf("%s\n", messageBuffer); + LocalFree(messageBuffer); +} + +unsigned WINAPI pipe_listener(void *parameters) { + while(1) { + HANDLE pipe = CreateNamedPipe( + pipeName, + PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + PIPE_BUFSIZE, + PIPE_BUFSIZE, + 0, + NULL); + BOOL clientConnected; + if( pipe == INVALID_HANDLE_VALUE ) { + printf("Error creating named pipe. Code %lu\n", GetLastError()); + return -1; + } + + // True if client connects *after* server called ConnectNamedPipe + // or *between* CreateNamedPipe and ConnectNamedPipe + clientConnected = + ConnectNamedPipe(pipe, NULL) ? TRUE : GetLastError()==ERROR_PIPE_CONNECTED; + printf("Client connected!\n"); + + if( !clientConnected ) { + printf("Failure while listening for clients. Code %lu\n", GetLastError()); + CloseHandle(pipe); + return -1; + } + printf("Create client thread\n"); + _beginthreadex(NULL, 0, fifo_rx, (PVOID)pipe, 0, 0); + } + + return 0; +} + +unsigned WINAPI fifo_rx(void *handle) { + printf("fifo_rx tid: %lu\n", GetCurrentThreadId()); + HANDLE pipe = (HANDLE) handle; + csp_packet_t *buf = csp_buffer_get(BUF_SIZE); + DWORD bytesRead; + BOOL readSuccess; + + while(1) { + readSuccess = + ReadFile(pipe, &buf->length, BUF_SIZE, &bytesRead, NULL); + if( !readSuccess || bytesRead == 0 ) { + csp_buffer_free(buf); + printError(); + break; + } + csp_qfifo_write(buf, &csp_if_fifo, NULL); + buf = csp_buffer_get(BUF_SIZE); + } + printf("Closing pipe to client\n"); + CloseHandle(pipe); + + return 0; +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/kiss.c b/gomspace/libgscsp/lib/libcsp/examples/kiss.c new file mode 100644 index 00000000..c95eb2aa --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/kiss.c @@ -0,0 +1,151 @@ +/** + * Build this example on linux with: + * ./waf configure --enable-examples --enable-if-kiss --with-driver-usart=linux --enable-crc32 clean build + */ + +#include +#include +#include + +#include +#include + +#define PORT 10 +#define MY_ADDRESS 1 + +#define SERVER_TIDX 0 +#define CLIENT_TIDX 1 +#define USART_HANDLE 0 + +CSP_DEFINE_TASK(task_server) { + int running = 1; + csp_socket_t *socket = csp_socket(CSP_SO_NONE); + csp_conn_t *conn; + csp_packet_t *packet; + csp_packet_t *response; + + response = csp_buffer_get(sizeof(csp_packet_t) + 2); + if( response == NULL ) { + fprintf(stderr, "Could not allocate memory for response packet!\n"); + return CSP_TASK_RETURN; + } + response->data[0] = 'O'; + response->data[1] = 'K'; + response->length = 2; + + csp_bind(socket, CSP_ANY); + csp_listen(socket, 5); + + printf("Server task started\r\n"); + + while(running) { + if( (conn = csp_accept(socket, 10000)) == NULL ) { + continue; + } + + while( (packet = csp_read(conn, 100)) != NULL ) { + switch( csp_conn_dport(conn) ) { + case PORT: + if( packet->data[0] == 'q' ) + running = 0; + csp_buffer_free(packet); + csp_send(conn, response, 1000); + break; + default: + csp_service_handler(conn, packet); + break; + } + } + + csp_close(conn); + } + + csp_buffer_free(response); + + return CSP_TASK_RETURN; +} + +CSP_DEFINE_TASK(task_client) { + + char outbuf = 'q'; + char inbuf[3] = {0}; + int pingResult; + + for(int i = 50; i <= 200; i+= 50) { + pingResult = csp_ping(MY_ADDRESS, 1000, 100, CSP_O_NONE); + printf("Ping with payload of %d bytes, took %d ms\n", i, pingResult); + csp_sleep_ms(1000); + } + csp_ps(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + csp_memfree(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + csp_buf_free(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + csp_uptime(MY_ADDRESS, 1000); + csp_sleep_ms(1000); + + csp_transaction(0, MY_ADDRESS, PORT, 1000, &outbuf, 1, inbuf, 2); + printf("Quit response from server: %s\n", inbuf); + + return CSP_TASK_RETURN; +} + +int main(int argc, char **argv) { + csp_debug_toggle_level(CSP_PACKET); + csp_debug_toggle_level(CSP_INFO); + + csp_buffer_init(10, 300); + csp_init(MY_ADDRESS); + + struct usart_conf conf; + +#if defined(CSP_WINDOWS) + conf.device = argc != 2 ? "COM4" : argv[1]; + conf.baudrate = CBR_9600; + conf.databits = 8; + conf.paritysetting = NOPARITY; + conf.stopbits = ONESTOPBIT; + conf.checkparity = FALSE; +#elif defined(CSP_POSIX) + conf.device = argc != 2 ? "/dev/ttyUSB0" : argv[1]; + conf.baudrate = 500000; +#elif defined(CSP_MACOSX) + conf.device = argc != 2 ? "/dev/tty.usbserial-FTSM9EGE" : argv[1]; + conf.baudrate = 115200; +#endif + + /* Run USART init */ + usart_init(&conf); + + /* Setup CSP interface */ + static csp_iface_t csp_if_kiss; + static csp_kiss_handle_t csp_kiss_driver; + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, usart_putc, usart_insert, "KISS"); + + /* Setup callback from USART RX to KISS RS */ + void my_usart_rx(uint8_t * buf, int len, void * pxTaskWoken) { + csp_kiss_rx(&csp_if_kiss, buf, len, pxTaskWoken); + } + usart_set_callback(my_usart_rx); + + csp_route_set(MY_ADDRESS, &csp_if_kiss, CSP_NODE_MAC); + csp_route_start_task(0, 0); + + csp_conn_print_table(); + csp_route_print_table(); + csp_route_print_interfaces(); + + csp_thread_handle_t handle_server; + csp_thread_create(task_server, "SERVER", 1000, NULL, 0, &handle_server); + csp_thread_handle_t handle_client; + csp_thread_create(task_client, "CLIENT", 1000, NULL, 0, &handle_client); + + /* Wait for program to terminate (ctrl + c) */ + while(1) { + csp_sleep_ms(1000000); + } + + return 0; + +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client.py b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client.py new file mode 100644 index 00000000..123ce36e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client.py @@ -0,0 +1,42 @@ +#!/usr/bin/python + +# libcsp must be build with at least these options to run this example client: +# ./waf distclean configure build --enable-bindings --enable-crc32 --enable-rdp --enable-if-zmq --with-driver-usart=linux --enable-if-kiss --enable-xtea --enable-if-can --enable-can-socketcan --enable-hmac --enable-examples + +# Can be run from root of libcsp like this: +# LD_LIBRARY_PATH=build PYTHONPATH=bindings/python:build python examples/python_bindings_example_client.py +# + +import os +import time +import libcsp as csp + + +if __name__ == "__main__": + + csp.buffer_init(10, 300) + csp.init(28) + csp.zmqhub_init(28, "localhost") + csp.rtable_set(27, 5, "ZMQHUB") + csp.route_start_task() + + ## allow router task startup + time.sleep(1) + + ## cmp_ident + (rc, host, model, rev, date, time) = csp.cmp_ident(27) + if rc == csp.CSP_ERR_NONE: + print (host, model, rev, date, time) + else: + print ("error in cmp_ident, rc=%i" % (rc)) + + ## transaction + outbuf = bytearray().fromhex('01') + inbuf = bytearray(1) + print ("using csp_transaction to send a single byte") + if csp.transaction(0, 27, 10, 1000, outbuf, inbuf) < 1: + print ("csp_transaction failed") + else: + print ("got reply, data=" + ''.join('{:02x}'.format(x) for x in inbuf)) + + diff --git a/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client_can.py b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client_can.py new file mode 100644 index 00000000..ec796572 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_client_can.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +# libcsp must be build with at least these options to run this example client: +# ./waf distclean configure build --enable-bindings --enable-crc32 --enable-rdp --enable-if-zmq --with-driver-usart=linux --enable-if-kiss --enable-xtea --enable-if-can --enable-can-socketcan --enable-hmac --enable-examples + +# Can be run from root of libcsp like this: +# LD_LIBRARY_PATH=build PYTHONPATH=bindings/python:build python examples/python_bindings_example_client.py +# + +import os +import time +import libcsp as csp + + +if __name__ == "__main__": + + csp.buffer_init(10, 300) + csp.init(28) + csp.can_socketcan_init("can0") + csp.rtable_set(4, 5, "CAN") + csp.route_start_task() + + ## allow router task startup + time.sleep(1) + + + node = 4 + if csp.ping(node) < 0: + print ("Unable to ping node %d"%(node)) + diff --git a/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_server.py b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_server.py new file mode 100644 index 00000000..3cf3f5da --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/python_bindings_example_server.py @@ -0,0 +1,72 @@ +#!/usr/bin/python + +# libcsp must be build with at least these options to run this example server: +# ./waf distclean configure build --enable-bindings --enable-crc32 --enable-rdp --enable-if-zmq --with-driver-usart=linux --enable-if-kiss --enable-xtea --enable-if-can --enable-can-socketcan --enable-hmac --enable-examples + +# Can be run from root of libcsp like this: +# LD_LIBRARY_PATH=build PYTHONPATH=bindings/python:build python examples/python_bindings_example_server.py +# + +import os +import time +import sys +import libcsp as csp +import subprocess + +if __name__ == "__main__": + + # start a zmqproxy to transport messages to and from the client + zmqp = subprocess.Popen('build/zmqproxy') + + # init csp + csp.buffer_init(10, 300) + csp.init(27) + csp.zmqhub_init(27, "localhost") + csp.rtable_set(28, 5, "ZMQHUB") + csp.route_start_task() + + # set identity + csp.set_hostname("test_service") + csp.set_model("bindings") + csp.set_revision("1.2.3") + + # and read it back + print (csp.get_hostname()) + print (csp.get_model()) + print (csp.get_revision()) + + # start listening for packets... + sock = csp.socket() + csp.bind(sock, csp.CSP_ANY) + csp.listen(sock) + while True: + conn = csp.accept(sock) + if not conn: + continue + + print ("connection: source=%i:%i, dest=%i:%i" % (csp.conn_src(conn), + csp.conn_sport(conn), + csp.conn_dst(conn), + csp.conn_dport(conn))) + + while True: + packet = csp.read(conn) + if not packet: + break + + if csp.conn_dport(conn) == 10: + data = bytearray(csp.packet_get_data(packet)) + length = csp.packet_get_length(packet) + print ("got packet, len=" + str(length) + ", data=" + ''.join('{:02x}'.format(x) for x in data)) + + data[0] = data[0] + 1 + reply_packet = csp.buffer_get(1) + if reply_packet: + csp.packet_set_data(reply_packet, data) + csp.sendto_reply(packet, reply_packet, csp.CSP_O_NONE) + + csp.buffer_free(packet) + else: + csp.service_handler(conn, packet) + csp.close(conn) + diff --git a/gomspace/libgscsp/lib/libcsp/examples/simple.c b/gomspace/libgscsp/lib/libcsp/examples/simple.c new file mode 100644 index 00000000..b996f8c1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/simple.c @@ -0,0 +1,200 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include + +/* Using un-exported header file. + * This is allowed since we are still in libcsp */ +#include + +/** Example defines */ +#define MY_ADDRESS 1 // Address of local CSP node +#define MY_PORT 10 // Port to send test traffic to + +CSP_DEFINE_TASK(task_server) { + + /* Create socket without any socket options */ + csp_socket_t *sock = csp_socket(CSP_SO_NONE); + + /* Bind all ports to socket */ + csp_bind(sock, CSP_ANY); + + /* Create 10 connections backlog queue */ + csp_listen(sock, 10); + + /* Pointer to current connection and packet */ + csp_conn_t *conn; + csp_packet_t *packet; + + /* Process incoming connections */ + while (1) { + + /* Wait for connection, 10000 ms timeout */ + if ((conn = csp_accept(sock, 10000)) == NULL) + continue; + + /* Read packets. Timout is 100 ms */ + while ((packet = csp_read(conn, 100)) != NULL) { + switch (csp_conn_dport(conn)) { + case MY_PORT: + /* Process packet here */ + printf("Packet received on MY_PORT: %s\r\n", (char *) packet->data); + csp_buffer_free(packet); + break; + + default: + /* Let the service handler reply pings, buffer use, etc. */ + csp_service_handler(conn, packet); + break; + } + } + + /* Close current connection, and handle next */ + csp_close(conn); + + } + + return CSP_TASK_RETURN; + +} + +CSP_DEFINE_TASK(task_client) { + + csp_packet_t * packet; + csp_conn_t * conn; + + while (1) { + + /** + * Try ping + */ + + csp_sleep_ms(1000); + + int result = csp_ping(MY_ADDRESS, 100, 100, CSP_O_NONE); + printf("Ping result %d [ms]\r\n", result); + + csp_sleep_ms(1000); + + /** + * Try data packet to server + */ + + /* Get packet buffer for data */ + packet = csp_buffer_get(100); + if (packet == NULL) { + /* Could not get buffer element */ + printf("Failed to get buffer element\n"); + return CSP_TASK_RETURN; + } + + /* Connect to host HOST, port PORT with regular UDP-like protocol and 1000 ms timeout */ + conn = csp_connect(CSP_PRIO_NORM, MY_ADDRESS, MY_PORT, 1000, CSP_O_NONE); + if (conn == NULL) { + /* Connect failed */ + printf("Connection failed\n"); + /* Remember to free packet buffer */ + csp_buffer_free(packet); + return CSP_TASK_RETURN; + } + + /* Copy dummy data to packet */ + const char *msg = "Hello World"; + strcpy((char *) packet->data, msg); + + /* Set packet length */ + packet->length = strlen(msg); + + /* Send packet */ + if (!csp_send(conn, packet, 1000)) { + /* Send failed */ + printf("Send failed\n"); + csp_buffer_free(packet); + } + + /* Close connection */ + csp_close(conn); + + } + + return CSP_TASK_RETURN; +} + +int main(int argc, char * argv[]) { + + /** + * Initialise CSP, + * No physical interfaces are initialised in this example, + * so only the loopback interface is registered. + */ + + /* Init buffer system with 10 packets of maximum 300 bytes each */ + printf("Initialising CSP\r\n"); + csp_buffer_init(5, 300); + + /* Init CSP with address MY_ADDRESS */ + csp_init(MY_ADDRESS); + + /* Start router task with 500 word stack, OS task priority 1 */ + csp_route_start_task(500, 1); + + /* Enable debug output from CSP */ + if ((argc > 1) && (strcmp(argv[1], "-v") == 0)) { + printf("Debug enabed\r\n"); + csp_debug_toggle_level(3); + csp_debug_toggle_level(4); + + printf("Conn table\r\n"); + csp_conn_print_table(); + + printf("Route table\r\n"); + csp_route_print_table(); + + printf("Interfaces\r\n"); + csp_route_print_interfaces(); + + } + + /** + * Initialise example threads, using pthreads. + */ + + /* Server */ + printf("Starting Server task\r\n"); + csp_thread_handle_t handle_server; + csp_thread_create(task_server, "SERVER", 1000, NULL, 0, &handle_server); + + /* Client */ + printf("Starting Client task\r\n"); + csp_thread_handle_t handle_client; + csp_thread_create(task_client, "SERVER", 1000, NULL, 0, &handle_client); + + /* Wait for execution to end (ctrl+c) */ + while(1) { + csp_sleep_ms(100000); + } + + return 0; + +} diff --git a/gomspace/libgscsp/lib/libcsp/examples/zmqproxy.c b/gomspace/libgscsp/lib/libcsp/examples/zmqproxy.c new file mode 100644 index 00000000..5e259579 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/examples/zmqproxy.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include +#include + +static void * task_capture(void *ctx) { + + /* Subscriber (RX) */ + void *subscriber = zmq_socket(ctx, ZMQ_SUB); + assert(zmq_connect(subscriber, "tcp://localhost:7000") == 0); + assert(zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0) == 0); + + while (1) { + zmq_msg_t msg; + zmq_msg_init_size(&msg, 1024); + + /* Receive data */ + if (zmq_msg_recv(&msg, subscriber, 0) < 0) { + zmq_msg_close(&msg); + csp_log_error("ZMQ: %s\r\n", zmq_strerror(zmq_errno())); + continue; + } + + int datalen = zmq_msg_size(&msg); + if (datalen < 5) { + csp_log_warn("ZMQ: Too short datalen: %u\r\n", datalen); + while(zmq_msg_recv(&msg, subscriber, ZMQ_NOBLOCK) > 0) + zmq_msg_close(&msg); + continue; + } + + /* Create new csp packet */ + csp_packet_t * packet = malloc(1024); + if (packet == NULL) { + zmq_msg_close(&msg); + continue; + } + + /* Copy the data from zmq to csp */ + char * satidptr = ((char *) &packet->id) - 1; + memcpy(satidptr, zmq_msg_data(&msg), datalen); + packet->length = datalen - 4 - 1; + + printf("Input: Src %u, Dst %u, Dport %u, Sport %u, Pri %u, Flags 0x%02X, Size %"PRIu16"\r\n", + packet->id.src, packet->id.dst, packet->id.dport, + packet->id.sport, packet->id.pri, packet->id.flags, packet->length); + + free(packet); + zmq_msg_close(&msg); + } +} + +int main(int argc, char ** argv) { + + /** + * ZMQ PROXY + */ + void * ctx = zmq_ctx_new(); + assert(ctx); + + void *frontend = zmq_socket(ctx, ZMQ_XSUB); + assert(frontend); + assert(zmq_bind (frontend, "tcp://*:6000") == 0); + + void *backend = zmq_socket(ctx, ZMQ_XPUB); + assert(backend); + assert(zmq_bind(backend, "tcp://*:7000") == 0); + + pthread_t capworker; + pthread_create(&capworker, NULL, task_capture, ctx); + + printf("Starting ZMQproxy\r\n"); + zmq_proxy(frontend, backend, NULL); + + printf("Closing ZMQproxy\r\n"); + zmq_ctx_destroy(ctx); + return 0; + +} diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_clock.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_clock.h new file mode 100644 index 00000000..3c19c887 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_clock.h @@ -0,0 +1,60 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CLOCK_H_ +#define _CSP_CLOCK_H_ + +/** + @file + + Clock API. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + Cross-platform timestamp. +*/ +typedef struct { + //! Seconds + uint32_t tv_sec; + //! Nano-seconds. + uint32_t tv_nsec; +} csp_timestamp_t; + +/** + Get time - must be implemented by the user. +*/ +__attribute__((weak)) extern void clock_get_time(csp_timestamp_t * time); + +/** + Set time - must be implemented by the user. +*/ +__attribute__((weak)) extern void clock_set_time(csp_timestamp_t * time); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_CLOCK_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_malloc.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_malloc.h new file mode 100644 index 00000000..12602d1b --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_malloc.h @@ -0,0 +1,39 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_MALLOC_H_ +#define _CSP_MALLOC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +void * csp_malloc(size_t size); +void csp_free(void * ptr); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_MALLOC_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_queue.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_queue.h new file mode 100644 index 00000000..3156c05e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_queue.h @@ -0,0 +1,49 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_QUEUE_H_ +#define _CSP_QUEUE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_QUEUE_FULL 0 +#define CSP_QUEUE_ERROR 0 +#define CSP_QUEUE_OK 1 +typedef void * csp_queue_handle_t; + +#include +#include + +csp_queue_handle_t csp_queue_create(int length, size_t item_size); +void csp_queue_remove(csp_queue_handle_t queue); +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout); +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken); +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout); +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken); +int csp_queue_size(csp_queue_handle_t handle); +int csp_queue_size_isr(csp_queue_handle_t handle); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_QUEUE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_semaphore.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_semaphore.h new file mode 100644 index 00000000..c8068da2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_semaphore.h @@ -0,0 +1,109 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_SEMAPHORE_H_ +#define _CSP_SEMAPHORE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/* POSIX interface */ +#if defined(CSP_POSIX) + +#include +#include + +#define CSP_SEMAPHORE_OK 1 +#define CSP_SEMAPHORE_ERROR 2 +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef sem_t csp_bin_sem_handle_t; +typedef pthread_mutex_t csp_mutex_t; + +#endif // CSP_POSIX + +/* MAC OS X interface */ +#if defined(CSP_MACOSX) + +#include +#include "posix/pthread_queue.h" + +#define CSP_SEMAPHORE_OK PTHREAD_QUEUE_OK +#define CSP_SEMAPHORE_ERROR PTHREAD_QUEUE_EMPTY +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef pthread_queue_t * csp_bin_sem_handle_t; +typedef pthread_queue_t * csp_mutex_t; + +#endif // CSP_MACOSX + +#if defined(CSP_WINDOWS) + +#include +#undef interface + +#define CSP_SEMAPHORE_OK 1 +#define CSP_SEMAPHORE_ERROR 2 +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef HANDLE csp_bin_sem_handle_t; +typedef HANDLE csp_mutex_t; + +#endif + +/* FreeRTOS interface */ +#if defined(CSP_FREERTOS) + +#include +#include + +#define CSP_SEMAPHORE_OK pdPASS +#define CSP_SEMAPHORE_ERROR pdFAIL +#define CSP_MUTEX_OK CSP_SEMAPHORE_OK +#define CSP_MUTEX_ERROR CSP_SEMAPHORE_ERROR + +typedef xSemaphoreHandle csp_bin_sem_handle_t; +typedef xSemaphoreHandle csp_mutex_t; + +#endif // CSP_FREERTOS + +int csp_mutex_create(csp_mutex_t * mutex); +int csp_mutex_remove(csp_mutex_t * mutex); +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout); +int csp_mutex_unlock(csp_mutex_t * mutex); +int csp_bin_sem_create(csp_bin_sem_handle_t * sem); +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem); +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout); +int csp_bin_sem_post(csp_bin_sem_handle_t * sem); +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_SEMAPHORE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_system.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_system.h new file mode 100644 index 00000000..c6c0e5af --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_system.h @@ -0,0 +1,74 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_SYSTEM_H_ +#define _CSP_SYSTEM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define COLOR_MASK_COLOR 0x0F +#define COLOR_MASK_MODIFIER 0xF0 + +typedef enum { + /* Colors */ + COLOR_RESET = 0xF0, + COLOR_BLACK = 0x01, + COLOR_RED = 0x02, + COLOR_GREEN = 0x03, + COLOR_YELLOW = 0x04, + COLOR_BLUE = 0x05, + COLOR_MAGENTA = 0x06, + COLOR_CYAN = 0x07, + COLOR_WHITE = 0x08, + /* Modifiers */ + COLOR_NORMAL = 0x0F, + COLOR_BOLD = 0x10, + COLOR_UNDERLINE = 0x20, + COLOR_BLINK = 0x30, + COLOR_HIDE = 0x40, +} csp_color_t; + +/** + * Writes out a task list into a pre-allocate buffer, + * use csp_sys_tasklist_size to get sizeof buffer to allocate + * @param out pointer to output buffer + * @return + */ +int csp_sys_tasklist(char * out); + +/** + * @return Size of tasklist buffer to allocate for the csp_sys_tasklist call + */ +int csp_sys_tasklist_size(void); + +uint32_t csp_sys_memfree(void); +int csp_sys_reboot(void); +int csp_sys_shutdown(void); +void csp_sys_set_color(csp_color_t color); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_SYSTEM_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_thread.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_thread.h new file mode 100644 index 00000000..3c6ea171 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_thread.h @@ -0,0 +1,100 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_THREAD_H_ +#define _CSP_THREAD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* POSIX interface */ +#if defined(CSP_POSIX) || defined(CSP_MACOSX) + +#include +#include + +#define csp_thread_exit() pthread_exit(NULL) + +typedef pthread_t csp_thread_handle_t; +typedef void * csp_thread_return_t; + +#define CSP_DEFINE_TASK(task_name) csp_thread_return_t task_name(void * param) +#define CSP_TASK_RETURN NULL + +#define csp_sleep_ms(time_ms) usleep(time_ms * 1000); + +#endif // CSP_POSIX + +/* Windows interface */ +#if defined(CSP_WINDOWS) + +#include +#undef interface +#include + +#define csp_thread_exit() _endthreadex(0) + +typedef HANDLE csp_thread_handle_t; +typedef unsigned int csp_thread_return_t; + +#define CSP_DEFINE_TASK(task_name) csp_thread_return_t __attribute__((stdcall)) task_name(void * param) +#define CSP_TASK_RETURN 0 + +#define csp_sleep_ms(time_ms) Sleep(time_ms); + +#endif // CSP_WINDOWS + +/* FreeRTOS interface */ +#if defined(CSP_FREERTOS) + +#include +#include + +#if INCLUDE_vTaskDelete +#define csp_thread_exit() vTaskDelete(NULL) +#else +#define csp_thread_exit() +#endif + +typedef xTaskHandle csp_thread_handle_t; +typedef void csp_thread_return_t; + +#define CSP_DEFINE_TASK(task_name) csp_thread_return_t task_name(void * param) +#define CSP_TASK_RETURN + +#define csp_sleep_ms(time_ms) vTaskDelay(time_ms / portTICK_RATE_MS); + +#endif // CSP_FREERTOS + +#ifndef CSP_WINDOWS +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle); +#else +int csp_thread_create(csp_thread_return_t (* routine)(void *)__attribute__((stdcall)), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_THREAD_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_time.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_time.h new file mode 100644 index 00000000..aa72ab8f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/csp_time.h @@ -0,0 +1,57 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_TIME_H_ +#define _CSP_TIME_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Blackfin/x86 on Linux */ +#if defined(CSP_POSIX) + +#include +#include +#include + +#endif // CSP_POSIX + +/* AVR/ARM on FreeRTOS */ +#if defined(CSP_FREERTOS) + +#include +#include + +#endif // CSP_FREERTOS + +uint32_t csp_get_ms(void); +uint32_t csp_get_ms_isr(void); +uint32_t csp_get_s(void); +uint32_t csp_get_s_isr(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_TIME_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/arch/posix/pthread_queue.h b/gomspace/libgscsp/lib/libcsp/include/csp/arch/posix/pthread_queue.h new file mode 100644 index 00000000..44ef596e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/arch/posix/pthread_queue.h @@ -0,0 +1,118 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _PTHREAD_QUEUE_H_ +#define _PTHREAD_QUEUE_H_ + +/** + @file + + Queue implemented using pthread locks and conds. + + Inspired by c-pthread-queue by Matthew Dickinson: http://code.google.com/p/c-pthread-queue/ +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +/** + Queue error codes. + @{ +*/ +/** + General error code - something went wrong. +*/ +#define PTHREAD_QUEUE_ERROR CSP_QUEUE_ERROR +/** + Queue is empty - cannot extract element. +*/ +#define PTHREAD_QUEUE_EMPTY CSP_QUEUE_ERROR +/** + Queue is full - cannot insert element. +*/ +#define PTHREAD_QUEUE_FULL CSP_QUEUE_ERROR +/** + Ok - no error. +*/ +#define PTHREAD_QUEUE_OK CSP_QUEUE_OK +/** @{ */ + +/** + Queue handle. +*/ +typedef struct pthread_queue_s { + //! Memory area. + void * buffer; + //! Memory size. + int size; + //! Item/element size. + int item_size; + //! Items/elements in queue. + int items; + //! Insert point. + int in; + //! Extract point. + int out; + //! Lock. + pthread_mutex_t mutex; + //! Wait because queue is full (insert). + pthread_cond_t cond_full; + //! Wait because queue is empty (extract). + pthread_cond_t cond_empty; +} pthread_queue_t; + +/** + Create queue. +*/ +pthread_queue_t * pthread_queue_create(int length, size_t item_size); + +/** + Delete queue. +*/ +void pthread_queue_delete(pthread_queue_t * q); + +/** + Enqueue/insert element. +*/ +int pthread_queue_enqueue(pthread_queue_t * queue, void * value, uint32_t timeout); + +/** + Dequeue/extract element. +*/ +int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout); + +/** + Return number of elements in the queue. +*/ +int pthread_queue_items(pthread_queue_t * queue); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _PTHREAD_QUEUE_H_ + diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_hmac.h b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_hmac.h new file mode 100644 index 00000000..8c3f5d6a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_hmac.h @@ -0,0 +1,73 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_HMAC_H_ +#define _CSP_HMAC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_HMAC_LENGTH 4 + +/** + * Append HMAC to packet + * @param packet Pointer to packet + * @param include_header use header in hmac calculation (this will not modify the flags field) + * @return 0 on success, negative on failure + */ +int csp_hmac_append(csp_packet_t * packet, bool include_header); + +/** + * Verify HMAC of packet + * @param packet Pointer to packet + * @param include_header use header in hmac calculation (this will not modify the flags field) + * @return 0 on success, negative on failure + */ +int csp_hmac_verify(csp_packet_t * packet, bool include_header); + +/** + * Calculate HMAC on buffer + * + * This function is used by append/verify but cal also be called separately. + * @param key HMAC key + * @param keylen HMAC key length + * @param data pointer to data + * @param datalen lehgth of data + * @param hmac output HMAC calculation (CSP_HMAC_LENGTH) + * @return 0 on success, negative on failure + */ +int csp_hmac_memory(const uint8_t * key, uint32_t keylen, const uint8_t * data, uint32_t datalen, uint8_t * hmac); + +/** + * Save a copy of the key string for use by the append/verify functions + * @param key HMAC key + * @param keylen HMAC key length + * @return Always returns 0 + */ +int csp_hmac_set_key(char * key, uint32_t keylen); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_HMAC_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_sha1.h b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_sha1.h new file mode 100644 index 00000000..aa7e7a0d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_sha1.h @@ -0,0 +1,81 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_SHA1_H_ +#define _CSP_SHA1_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* The SHA1 block and message digest size in bytes */ +#define SHA1_BLOCKSIZE 64 +#define SHA1_DIGESTSIZE 20 + +/** + SHA1 state structure +*/ +typedef struct { + //! Internal SHA1 state. + uint64_t length; + //! Internal SHA1 state. + uint32_t state[5]; + //! Internal SHA1 state. + uint32_t curlen; + //! Internal SHA1 state. + uint8_t buf[SHA1_BLOCKSIZE]; +} csp_sha1_state; + +/** + * Initialize the hash state + * @param sha1 The hash state you wish to initialize + */ +void csp_sha1_init(csp_sha1_state * sha1); + +/** + * Process a block of memory though the hash + * @param sha1 The hash state + * @param in The data to hash + * @param inlen The length of the data (octets) + */ +void csp_sha1_process(csp_sha1_state * sha1, const uint8_t * in, uint32_t inlen); + +/** + * Terminate the hash to get the digest + * @param sha1 The hash state + * @param out [out] The destination of the hash (20 bytes) + */ +void csp_sha1_done(csp_sha1_state * sha1, uint8_t * out); + +/** + * Calculate SHA1 hash of block of memory. + * @param msg Pointer to message buffer + * @param len Length of message + * @param sha1 Pointer to SHA1 output buffer. Must be 20 bytes or more! + */ +void csp_sha1_memory(const uint8_t * msg, uint32_t len, uint8_t * hash); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_SHA1_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_xtea.h b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_xtea.h new file mode 100644 index 00000000..f740b8d5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/crypto/csp_xtea.h @@ -0,0 +1,52 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_XTEA_H_ +#define _CSP_XTEA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_XTEA_IV_LENGTH 8 + +/** + * XTEA encrypt byte array + * @param plain Pointer to plain text + * @param len Length of plain text + * @param iv Initialization vector + */ +int csp_xtea_encrypt(uint8_t * plain, const uint32_t len, uint32_t iv[2]); + +/** + * Decrypt XTEA encrypted byte array + * @param cipher Pointer to cipher text + * @param len Length of plain text + * @param iv Initialization vector + */ +int csp_xtea_decrypt(uint8_t * cipher, const uint32_t len, uint32_t iv[2]); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_XTEA_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp.h new file mode 100644 index 00000000..6962195b --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp.h @@ -0,0 +1,545 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_H_ +#define _CSP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes */ +#include + +#include + +/* CSP includes */ +#include "csp_types.h" +#include "csp_platform.h" +#include "csp_error.h" +#include "csp_debug.h" +#include "csp_buffer.h" +#include "csp_rtable.h" +#include "csp_iflist.h" + +/** csp_init + * Start up the can-space protocol + * @param my_node_address The CSP node address + */ +int csp_init(uint8_t my_node_address); + +/** csp_set_address + * Set the systems own address + * @param addr The new address of the system + */ +void csp_set_address(uint8_t addr); + +/** csp_get_address + * Get the systems own address + * @return The current address of the system + */ +uint8_t csp_get_address(void); + +/** csp_set_hostname + * Set subsystem hostname. + * This function takes a pointer to a string, which should remain static + * @param hostname Hostname to set + */ +void csp_set_hostname(const char *hostname); + +/** csp_get_hostname + * Get current subsystem hostname. + * @return Pointer to char array with current hostname. + */ +const char *csp_get_hostname(void); + +/** csp_set_model + * Set subsystem model name. + * This function takes a pointer to a string, which should remain static + * @param model Model name to set + */ +void csp_set_model(const char *model); + +/** csp_get_model + * Get current model name. + * @return Pointer to char array with current model name. + */ +const char *csp_get_model(void); + +/** csp_set_revision + * Set subsystem revision. This can be used to override the CMP revision field. + * This function takes a pointer to a string, which should remain static + * @param revision Revision name to set + */ +void csp_set_revision(const char *revision); + +/** csp_get_revision + * Get subsystem revision. + * @return Pointer to char array with software revision. + */ +const char *csp_get_revision(void); + +/** csp_socket + * Create CSP socket endpoint + * @param opts Socket options + * @return Pointer to socket on success, NULL on failure + */ +csp_socket_t *csp_socket(uint32_t opts); + +/** + * Wait for a new connection on a socket created by csp_socket + * @param socket Socket to accept connections on + * @param timeout use CSP_MAX_DELAY for infinite timeout + * @return Return pointer to csp_conn_t or NULL if timeout was reached + */ +csp_conn_t *csp_accept(csp_socket_t *socket, uint32_t timeout); + +/** + * Read data from a connection + * This fuction uses the RX queue of a connection to receive a packet + * If no packet is available and a timeout has been specified + * The call will block. + * Do NOT call this from ISR + * @param conn pointer to connection + * @param timeout timeout in ms, use CSP_MAX_DELAY for infinite blocking time + * @return Returns pointer to csp_packet_t, which you MUST free yourself, either by calling csp_buffer_free() or reusing the buffer for a new csp_send. + */ +csp_packet_t *csp_read(csp_conn_t *conn, uint32_t timeout); + +/** + * Send a packet on an already established connection + * @param conn pointer to connection + * @param packet pointer to packet, + * @param timeout a timeout to wait for TX to complete. NOTE: not all underlying drivers supports flow-control. + * @return returns 1 if successful and 0 otherwise. you MUST free the frame yourself if the transmission was not successful. + */ +int csp_send(csp_conn_t *conn, csp_packet_t *packet, uint32_t timeout); + +/** + * Send a packet on an already established connection, and change the default priority of the connection + * + * @note When using this function, the priority of the connection will change. If you need to change it back + * use another call to csp_send_prio, or ensure that all packets sent on a given connection is using send_prio call. + * + * @param prio csp priority + * @param conn pointer to connection + * @param packet pointer to packet, + * @param timeout a timeout to wait for TX to complete. NOTE: not all underlying drivers supports flow-control. + * @return returns 1 if successful and 0 otherwise. you MUST free the frame yourself if the transmission was not successful. + */ +int csp_send_prio(uint8_t prio, csp_conn_t *conn, csp_packet_t *packet, uint32_t timeout); + +/** + * Perform an entire request/reply transaction + * Copies both input buffer and reply to output buffeer. + * Also makes the connection and closes it again + * @param prio CSP Prio + * @param dest CSP Dest + * @param port CSP Port + * @param timeout timeout in ms + * @param outbuf pointer to outgoing data buffer + * @param outlen length of request to send + * @param inbuf pointer to incoming data buffer + * @param inlen length of expected reply, -1 for unknown size (note inbuf MUST be large enough) + * @return Return 1 or reply size if successful, 0 if error or incoming length does not match or -1 if timeout was reached + */ +int csp_transaction(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen); + +/** + * Perform an entire request/reply transaction + * Copies both input buffer and reply to output buffeer. + * Also makes the connection and closes it again + * @param prio CSP Prio + * @param dest CSP Dest + * @param port CSP Port + * @param timeout timeout in ms + * @param outbuf pointer to outgoing data buffer + * @param outlen length of request to send + * @param inbuf pointer to incoming data buffer + * @param inlen length of expected reply, -1 for unknown size (note inbuf MUST be large enough) + * @param opts Connection options. + * @return Return 1 or reply size if successful, 0 if error or incoming length does not match or -1 if timeout was reached + */ +int csp_transaction2(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen, uint32_t opts); + +/** + * Use an existing connection to perform a transaction, + * This is only possible if the next packet is on the same port and destination! + * @param conn pointer to connection structure + * @param timeout timeout in ms + * @param outbuf pointer to outgoing data buffer + * @param outlen length of request to send + * @param inbuf pointer to incoming data buffer + * @param inlen length of expected reply, -1 for unknown size (note inbuf MUST be large enough) + * @return + */ +int csp_transaction_persistent(csp_conn_t *conn, uint32_t timeout, void *outbuf, int outlen, void *inbuf, int inlen); + +/** + * Read data from a connection-less server socket + * This fuction uses the socket directly to receive a frame + * If no packet is available and a timeout has been specified the call will block. + * Do NOT call this from ISR + * @return Returns pointer to csp_packet_t, which you MUST free yourself, either by calling csp_buffer_free() or reusing the buffer for a new csp_send. + */ +csp_packet_t *csp_recvfrom(csp_socket_t *socket, uint32_t timeout); + +/** + * Send a packet without previously opening a connection + * @param prio CSP_PRIO_x + * @param dest destination node + * @param dport destination port + * @param src_port source port + * @param opts CSP_O_x + * @param packet pointer to packet + * @param timeout timeout used by interfaces with blocking send + * @return -1 if error (you must free packet), 0 if OK (you must discard pointer) + */ +int csp_sendto(uint8_t prio, uint8_t dest, uint8_t dport, uint8_t src_port, uint32_t opts, csp_packet_t *packet, uint32_t timeout); + +/** + * Send a packet as a direct reply to the source of an incoming packet, + * but still without holding an entire connection + * @param request_packet pointer to packet to reply to + * @param reply_packet actual reply data + * @param opts CSP_O_x + * @param timeout timeout used by interfaces with blocking send + * @return -1 if error (you must free packet), 0 if OK (you must discard pointer) + */ +int csp_sendto_reply(csp_packet_t * request_packet, csp_packet_t * reply_packet, uint32_t opts, uint32_t timeout); + +/** csp_connect + * Used to establish outgoing connections + * This function searches the port table for free slots and finds an unused + * connection from the connection pool + * There is no handshake in the CSP protocol + * @param prio Connection priority. + * @param dest Destination address. + * @param dport Destination port. + * @param timeout Timeout in ms. + * @param opts Connection options. + * @return a pointer to a new connection or NULL + */ +csp_conn_t *csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts); + +/** csp_close + * Closes a given connection and frees buffers used. + * @param conn pointer to connection structure + * @return CSP_ERR_NONE if connection was closed. Otherwise, an err code is returned. + */ +int csp_close(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return destination port of an incoming connection + */ +int csp_conn_dport(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return source port of an incoming connection + */ +int csp_conn_sport(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return destination address of an incoming connection + */ +int csp_conn_dst(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return source address of an incoming connection + */ +int csp_conn_src(csp_conn_t *conn); + +/** + * @param conn pointer to connection structure + * @return flags field of an incoming connection + */ +int csp_conn_flags(csp_conn_t *conn); + +/** + * Set socket to listen for incoming connections + * @param socket Socket to enable listening on + * @param conn_queue_length Lenght of backlog connection queue + * @return 0 on success, -1 on error. + */ +int csp_listen(csp_socket_t *socket, size_t conn_queue_length); + +/** + * Bind port to socket + * @param socket Socket to bind port to + * @param port Port number to bind + * @return 0 on success, -1 on error. + */ +int csp_bind(csp_socket_t *socket, uint8_t port); + +/** + * Start the router task. + * @param task_stack_size The number of portStackType to allocate. This only affects FreeRTOS systems. + * @param priority The OS task priority of the router + */ +int csp_route_start_task(unsigned int task_stack_size, unsigned int priority); + +/** + * Call the router worker function manually (without the router task) + * This must be run inside a loop or called periodically for the csp router to work. + * Use this function instead of calling and starting the router task. + * @param timeout max blocking time + * @return -1 if no packet was processed, 0 otherwise + */ +int csp_route_work(uint32_t timeout); + +/** + * Start the bridge task. + * @param task_stack_size The number of portStackType to allocate. This only affects FreeRTOS systems. + * @param priority The OS task priority of the router + * @param _if_a pointer to first side + * @param _if_b pointer to second side + * @return CSP_ERR type + */ +int csp_bridge_start(unsigned int task_stack_size, unsigned int task_priority, csp_iface_t * _if_a, csp_iface_t * _if_b); + +/** + * Enable promiscuous mode packet queue + * This function is used to enable promiscuous mode for the router. + * If enabled, a copy of all incoming packets are placed in a queue + * that can be read with csp_promisc_get(). Not all interface drivers + * support promiscuous mode. + * + * @param buf_size Size of buffer for incoming packets + */ +int csp_promisc_enable(unsigned int buf_size); + +/** + * Disable promiscuous mode. + * If the queue was initialised prior to this, it can be re-enabled + * by calling promisc_enable(0) + */ +void csp_promisc_disable(void); + +/** + * Get packet from promiscuous mode packet queue + * Returns the first packet from the promiscuous mode packet queue. + * The queue is FIFO, so the returned packet is the oldest one + * in the queue. + * + * @param timeout Timeout in ms to wait for a new packet + */ +csp_packet_t *csp_promisc_read(uint32_t timeout); + +/** + * Send multiple packets using the simple fragmentation protocol + * CSP will add total size and offset to all packets + * This can be read by the client using the csp_sfp_recv, if the CSP_FFRAG flag is set + * @param conn pointer to connection + * @param data pointer to data to send + * @param totalsize size of data to send + * @param mtu maximum transfer unit + * @param timeout timeout in ms to wait for csp_send() + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_send(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout); + +/** + * Same as csp_sfp_send but with option to supply your own memcpy function. + * This is usefull if you wish to send data stored in flash memory or another location + * @param conn pointer to connection + * @param data pointer to data to send + * @param totalsize size of data to send + * @param mtu maximum transfer unit + * @param timeout timeout in ms to wait for csp_send() + * @param memcpyfcn, pointer to memcpy function + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout, void * (*memcpyfcn)(void *, const void *, size_t)); + +/** + * This is the counterpart to the csp_sfp_send function + * @param conn pointer to active conn, on which you expect to receive sfp packed data + * @param dataout pointer to NULL pointer, whill be overwritten with malloc pointer + * @param datasize actual size of received data + * @param timeout timeout in ms to wait for csp_recv() + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_recv(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout); + +/** + * This is the counterpart to the csp_sfp_send function + * @param conn pointer to active conn, on which you expect to receive sfp packed data + * @param dataout pointer to NULL pointer, whill be overwritten with malloc pointer + * @param datasize actual size of received data + * @param timeout timeout in ms to wait for csp_recv() + * @param first_packet This is a pointer to the first SFP packet (previously received with csp_read) + * @return 0 if OK, -1 if ERR + */ +int csp_sfp_recv_fp(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout, csp_packet_t * first_packet); + +/** + * If the given packet is a service-request (that is uses one of the csp service ports) + * it will be handled according to the CSP service handler. + * This function will either use the packet buffer or delete it, + * so this function is typically called in the last "default" clause of + * a switch/case statement in a csp_listener task. + * In order to listen to csp service ports, bind your listener to the CSP_ANY port. + * This function may only be called from task context. + * @param conn Pointer to the new connection + * @param packet Pointer to the first packet, obtained by using csp_read() + */ +void csp_service_handler(csp_conn_t *conn, csp_packet_t *packet); + +/** + * Send a single ping/echo packet + * @param node node id + * @param timeout timeout in ms + * @param size size of packet to transmit + * @param conn_options csp connection options + * @return >0 = Echo time in ms, -1 = ERR + */ +int csp_ping(uint8_t node, uint32_t timeout, unsigned int size, uint8_t conn_options); + +/** + * Send a single ping/echo packet without waiting for reply + * @param node node id + */ +void csp_ping_noreply(uint8_t node); + +/** + * Request process list. + * @note This is only available for FreeRTOS systems + * @param node node id + * @param timeout timeout in ms + */ +void csp_ps(uint8_t node, uint32_t timeout); + +/** + * Request amount of free memory + * @param node node id + * @param timeout timeout in ms + */ +void csp_memfree(uint8_t node, uint32_t timeout); + +/** + * Request number of free buffer elements + * @param node node id + * @param timeout timeout in ms + */ +void csp_buf_free(uint8_t node, uint32_t timeout); + +/** + * Reboot subsystem + * @param node node id + */ +void csp_reboot(uint8_t node); + +/** + * Shutdown subsystem + * @param node node id + */ +void csp_shutdown(uint8_t node); + +/** + * Request subsystem uptime + * @param node node id + * @param timeout timeout in ms + */ +void csp_uptime(uint8_t node, uint32_t timeout); + +/** + * Set RDP options + * @param window_size Window size + * @param conn_timeout_ms Connection timeout in ms + * @param packet_timeout_ms Packet timeout in ms + * @param delayed_acks Enable/disable delayed acknowledgements + * @param ack_timeout Acknowledgement timeout when delayed ACKs is enabled + * @param ack_delay_count Send acknowledgement for every ack_delay_count packets + */ +void csp_rdp_set_opt(unsigned int window_size, unsigned int conn_timeout_ms, + unsigned int packet_timeout_ms, unsigned int delayed_acks, + unsigned int ack_timeout, unsigned int ack_delay_count); + +/** + * Get RDP options + * @param window_size Window size + * @param conn_timeout_ms Connection timeout in ms + * @param packet_timeout_ms Packet timeout in ms + * @param delayed_acks Enable/disable delayed acknowledgements + * @param ack_timeout Acknowledgement timeout when delayed ACKs is enabled + * @param ack_delay_count Send acknowledgement for every ack_delay_count packets + */ +void csp_rdp_get_opt(unsigned int *window_size, unsigned int *conn_timeout_ms, + unsigned int *packet_timeout_ms, unsigned int *delayed_acks, + unsigned int *ack_timeout, unsigned int *ack_delay_count); + +/** + * Set XTEA key + * @param key Pointer to key array + * @param keylen Length of key + * @return 0 if key was successfully set, -1 otherwise + */ +int csp_xtea_set_key(char *key, uint32_t keylen); + +/** + * Set HMAC key + * @param key Pointer to key array + * @param keylen Length of key + * @return 0 if key was successfully set, -1 otherwise + */ +int csp_hmac_set_key(char *key, uint32_t keylen); + +/** + * Print connection table + */ +void csp_conn_print_table(void); +int csp_conn_print_table_str(char * str_buf, int str_size); + +/** + * Print buffer usage table + */ +void csp_buffer_print_table(void); + +/** + * Hex dump to stdout + */ +void csp_hex_dump(const char *desc, void *addr, int len); + +#ifdef __AVR__ +typedef uint32_t csp_memptr_t; +#else +typedef void * csp_memptr_t; +#endif + +typedef csp_memptr_t (*csp_memcpy_fnc_t)(csp_memptr_t, const csp_memptr_t, size_t); +void csp_cmp_set_memcpy(csp_memcpy_fnc_t fnc); + +/** + * Set csp_debug hook function + * @param f Hook function + */ +#include +typedef void (*csp_debug_hook_func_t)(csp_debug_level_t level, const char *format, va_list args); +void csp_debug_hook_set(csp_debug_hook_func_t f); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_buffer.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_buffer.h new file mode 100644 index 00000000..9ed6df77 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_buffer.h @@ -0,0 +1,92 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_BUFFER_H_ +#define _CSP_BUFFER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Start the buffer handling system + * You must specify the number for buffers and the size. All buffers are fixed + * size so you must specify the size of your largest buffer. + * + * @param count Number of buffers to allocate + * @param size Buffer size in bytes. + * + * @return CSP_ERR_NONE if malloc() succeeded, CSP_ERR message otherwise. + */ +int csp_buffer_init(int count, int size); + +/** + * Get a reference to a free buffer. This function can only be called + * from task context. + * + * @param size Specify what data-size you will put in the buffer + * @return pointer to a free csp_packet_t or NULL if out of memory + */ +void * csp_buffer_get(size_t size); + +/** + * Get a reference to a free buffer. This function can only be called + * from interrupt context. + * + * @param buf_size Specify what data-size you will put in the buffer + * @return pointer to a free csp_packet_t or NULL if out of memory + */ +void * csp_buffer_get_isr(size_t buf_size); + +/** + * Free a buffer after use. + * @param packet pointer to memory area, must be acquired by csp_buffer_get(). + */ +void csp_buffer_free(void *packet); + +/** + * Free a buffer after use in ISR context. + * @param packet pointer to memory area, must be acquired by csp_buffer_get(). + */ +void csp_buffer_free_isr(void *packet); + +/** + * Clone an existing packet and increase/decrease cloned packet size. + * @param buffer Existing buffer to clone. + */ +void * csp_buffer_clone(void *buffer); + +/** + * Return how many buffers that are currently free. + * @return number of free buffers + */ +int csp_buffer_remaining(void); + +/** + * Return the size of the CSP buffers + * @return size of CSP buffers + */ +int csp_buffer_size(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_BUFFER_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_cmp.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_cmp.h new file mode 100644 index 00000000..114a8eab --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_cmp.h @@ -0,0 +1,189 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CMP_H_ +#define _CSP_CMP_H_ + +/** + @file + CSP management protocol (CMP). +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** + CMP type. + @{ +*/ +/** + CMP request. +*/ +#define CSP_CMP_REQUEST 0x00 +/** + CMP reply. +*/ +#define CSP_CMP_REPLY 0xff +/**@}*/ + +/** + CMP requests. + @{ +*/ +/** + CMP request codes. +*/ +/** + Request identification, compile time, revision, name. +*/ +#define CSP_CMP_IDENT 1 +/** + Set/configure routing. +*/ +#define CSP_CMP_ROUTE_SET 2 +/** + Request interface statistics. +*/ +#define CSP_CMP_IF_STATS 3 +/** + Peek/read data from memory. +*/ +#define CSP_CMP_PEEK 4 +/** + Poke/write data from memory. +*/ +#define CSP_CMP_POKE 5 +/** + Get/set clock. +*/ +#define CSP_CMP_CLOCK 6 +/**@}*/ + +/** + CMP identification - max revision length. +*/ +#define CSP_CMP_IDENT_REV_LEN 20 +/** + CMP identification - max date length. +*/ +#define CSP_CMP_IDENT_DATE_LEN 12 +/** + CMP identification - max time length. +*/ +#define CSP_CMP_IDENT_TIME_LEN 9 + +/** + CMP interface statistics - max interface name length. +*/ +#define CSP_CMP_ROUTE_IFACE_LEN 11 + +/** + CMP peek/read memeory - max read length. +*/ +#define CSP_CMP_PEEK_MAX_LEN 200 + +/** + CMP poke/write memeory - max write length. +*/ +#define CSP_CMP_POKE_MAX_LEN 200 + +/** + CSP management protocol description. +*/ +struct csp_cmp_message { + //! CMP request type. + uint8_t type; + //! CMP request code. + uint8_t code; + union { + struct { + char hostname[CSP_HOSTNAME_LEN]; + char model[CSP_MODEL_LEN]; + char revision[CSP_CMP_IDENT_REV_LEN]; + char date[CSP_CMP_IDENT_DATE_LEN]; + char time[CSP_CMP_IDENT_TIME_LEN]; + } ident; + struct { + uint8_t dest_node; + uint8_t next_hop_mac; + char interface[CSP_CMP_ROUTE_IFACE_LEN]; + } route_set; + struct __attribute__((__packed__)) { + char interface[CSP_CMP_ROUTE_IFACE_LEN]; + uint32_t tx; + uint32_t rx; + uint32_t tx_error; + uint32_t rx_error; + uint32_t drop; + uint32_t autherr; + uint32_t frame; + uint32_t txbytes; + uint32_t rxbytes; + uint32_t irq; + } if_stats; + struct { + uint32_t addr; + uint8_t len; + char data[CSP_CMP_PEEK_MAX_LEN]; + } peek; + struct { + uint32_t addr; + uint8_t len; + char data[CSP_CMP_POKE_MAX_LEN]; + } poke; + csp_timestamp_t clock; + }; +} __attribute__ ((packed)); + +/** + Macro for calculating size of management message. +*/ +#define CMP_SIZE(_memb) (sizeof(((struct csp_cmp_message *)0)->_memb) + sizeof(uint8_t) + sizeof(uint8_t)) + +/** + Generic send management message request. +*/ +int csp_cmp(uint8_t node, uint32_t timeout, uint8_t code, int membsize, struct csp_cmp_message *msg); + +/** + Macro for defining management handling function. +*/ +#define CMP_MESSAGE(_code, _memb) \ +static inline int csp_cmp_##_memb(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg) { \ + return csp_cmp(node, timeout, _code, CMP_SIZE(_memb), msg); \ +} + +CMP_MESSAGE(CSP_CMP_IDENT, ident) +CMP_MESSAGE(CSP_CMP_ROUTE_SET, route_set) +CMP_MESSAGE(CSP_CMP_IF_STATS, if_stats) +CMP_MESSAGE(CSP_CMP_PEEK, peek) +CMP_MESSAGE(CSP_CMP_POKE, poke) +CMP_MESSAGE(CSP_CMP_CLOCK, clock) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_CMP_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_crc32.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_crc32.h new file mode 100644 index 00000000..a474eaf8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_crc32.h @@ -0,0 +1,63 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CRC32_H_ +#define _CSP_CRC32_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Generate precomputed CRC32 table + */ +void csp_crc32_gentab(void); + +/** + * Append CRC32 checksum to packet + * @param packet Packet to append checksum + * @param include_header use header in calculation (this will not modify the flags field) + * @return 0 on success, -1 on error + */ +int csp_crc32_append(csp_packet_t * packet, bool include_header); + +/** + * Verify CRC32 checksum on packet + * @param packet Packet to verify + * @param include_header use header in calculation (this will not modify the flags field) + * @return 0 if checksum is valid, -1 otherwise + */ +int csp_crc32_verify(csp_packet_t * packet, bool include_header); + +/** + * Calculate checksum for a given memory area + * @param data pointer to memory + * @param length length of memory to do checksum on + * @return return uint32_t checksum + */ +uint32_t csp_crc32_memory(const uint8_t * data, uint32_t length); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_CRC32_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_debug.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_debug.h new file mode 100644 index 00000000..64429d49 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_debug.h @@ -0,0 +1,150 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_DEBUG_H_ +#define _CSP_DEBUG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Debug levels */ +typedef enum { + CSP_ERROR = 0, + CSP_WARN = 1, + CSP_INFO = 2, + CSP_BUFFER = 3, + CSP_PACKET = 4, + CSP_PROTOCOL = 5, + CSP_LOCK = 6, +} csp_debug_level_t; + +/* Extract filename component from path */ +#define BASENAME(_file) ((strrchr(_file, '/') ? : (strrchr(_file, '\\') ? : _file)) + 1) + +/* Implement csp_assert_fail_action to override default failure action */ +extern void __attribute__((weak)) csp_assert_fail_action(const char *assertion, const char *file, int line); + +#ifndef NDEBUG + #define csp_assert(exp) \ + do { \ + if (!(exp)) { \ + const char *assertion = #exp; \ + const char *file = BASENAME(__FILE__); \ + int line = __LINE__; \ + printf("\E[1;31m[%02" PRIu8 "] Assertion \'%s\' failed in %s:%d\E[0m\r\n", \ + csp_get_address(), assertion, file, line); \ + if (csp_assert_fail_action) \ + csp_assert_fail_action(assertion, file, line); \ + } \ + } while (0) +#else + #define csp_assert(...) do {} while (0) +#endif + +#ifdef __AVR__ + #include + #include + #define CONSTSTR(data) PSTR(data) + #undef printf + #undef sscanf + #undef scanf + #undef sprintf + #undef snprintf + #define printf(s, ...) printf_P(PSTR(s), ## __VA_ARGS__) + #define sscanf(buf, s, ...) sscanf_P(buf, PSTR(s), ## __VA_ARGS__) + #define scanf(s, ...) scanf_P(PSTR(s), ## __VA_ARGS__) + #define sprintf(buf, s, ...) sprintf_P(buf, PSTR(s), ## __VA_ARGS__) + #define snprintf(buf, size, s, ...) snprintf_P(buf, size, PSTR(s), ## __VA_ARGS__) +#else + #define CONSTSTR(data) data +#endif + +#ifdef CSP_DEBUG + #define csp_debug(level, format, ...) do { do_csp_debug(level, CONSTSTR(format), ##__VA_ARGS__); } while(0) +#else + #define csp_debug(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_ERROR + #define csp_log_error(format, ...) csp_debug(CSP_ERROR, format, ##__VA_ARGS__) +#else + #define csp_log_error(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_WARN + #define csp_log_warn(format, ...) csp_debug(CSP_WARN, format, ##__VA_ARGS__) +#else + #define csp_log_warn(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_INFO + #define csp_log_info(format, ...) csp_debug(CSP_INFO, format, ##__VA_ARGS__) +#else + #define csp_log_info(...) do {} while (0) +#endif + +#ifdef CSP_LOG_LEVEL_DEBUG + #define csp_log_buffer(format, ...) csp_debug(CSP_BUFFER, format, ##__VA_ARGS__) + #define csp_log_packet(format, ...) csp_debug(CSP_PACKET, format, ##__VA_ARGS__) + #define csp_log_protocol(format, ...) csp_debug(CSP_PROTOCOL, format, ##__VA_ARGS__) + #define csp_log_lock(format, ...) csp_debug(CSP_LOCK, format, ##__VA_ARGS__) +#else + #define csp_log_buffer(...) do {} while (0) + #define csp_log_packet(...) do {} while (0) + #define csp_log_protocol(...) do {} while (0) + #define csp_log_lock(...) do {} while (0) +#endif + +/** + * This function should not be used directly, use csp_log_() macro instead + * @param level + * @param format + */ +void do_csp_debug(csp_debug_level_t level, const char *format, ...); + +/** + * Toggle debug level on/off + * @param level Level to toggle + */ +void csp_debug_toggle_level(csp_debug_level_t level); + +/** + * Set debug level + * @param level Level to set + * @param value New level value + */ +void csp_debug_set_level(csp_debug_level_t level, bool value); + +/** + * Get current debug level value + * @param level Level value to get + * @return Level value + */ +int csp_debug_get_level(csp_debug_level_t level); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_DEBUG_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_endian.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_endian.h new file mode 100644 index 00000000..e63a73c2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_endian.h @@ -0,0 +1,170 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_ENDIAN_H_ +#define _CSP_ENDIAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Convert 16-bit integer from host byte order to network byte order + * @param h16 Host byte order 16-bit integer + */ +uint16_t csp_hton16(uint16_t h16); + +/** + * Convert 16-bit integer from host byte order to host byte order + * @param n16 Network byte order 16-bit integer + */ +uint16_t csp_ntoh16(uint16_t n16); + +/** + * Convert 32-bit integer from host byte order to network byte order + * @param h32 Host byte order 32-bit integer + */ +uint32_t csp_hton32(uint32_t h32); + +/** + * Convert 32-bit integer from host byte order to host byte order + * @param n32 Network byte order 32-bit integer + */ +uint32_t csp_ntoh32(uint32_t n32); + +/** + * Convert 64-bit integer from host byte order to network byte order + * @param h64 Host byte order 64-bit integer + */ +uint64_t csp_hton64(uint64_t h64); + +/** + * Convert 64-bit integer from host byte order to host byte order + * @param n64 Network byte order 64-bit integer + */ +uint64_t csp_ntoh64(uint64_t n64); + +/** + * Convert 16-bit integer from host byte order to big endian byte order + * @param h16 Host byte order 16-bit integer + */ +uint16_t csp_htobe16(uint16_t h16); + +/** + * Convert 16-bit integer from host byte order to little endian byte order + * @param h16 Host byte order 16-bit integer + */ +uint16_t csp_htole16(uint16_t h16); + +/** + * Convert 16-bit integer from big endian byte order to little endian byte order + * @param be16 Big endian byte order 16-bit integer + */ +uint16_t csp_betoh16(uint16_t be16); + +/** + * Convert 16-bit integer from little endian byte order to host byte order + * @param le16 Little endian byte order 16-bit integer + */ +uint16_t csp_letoh16(uint16_t le16); + +/** + * Convert 32-bit integer from host byte order to big endian byte order + * @param h32 Host byte order 32-bit integer + */ +uint32_t csp_htobe32(uint32_t h32); + +/** + * Convert 32-bit integer from little endian byte order to host byte order + * @param h32 Host byte order 32-bit integer + */ +uint32_t csp_htole32(uint32_t h32); + +/** + * Convert 32-bit integer from big endian byte order to host byte order + * @param be32 Big endian byte order 32-bit integer + */ +uint32_t csp_betoh32(uint32_t be32); + +/** + * Convert 32-bit integer from little endian byte order to host byte order + * @param le32 Little endian byte order 32-bit integer + */ +uint32_t csp_letoh32(uint32_t le32); + +/** + * Convert 64-bit integer from host byte order to big endian byte order + * @param h64 Host byte order 64-bit integer + */ +uint64_t csp_htobe64(uint64_t h64); + +/** + * Convert 64-bit integer from host byte order to little endian byte order + * @param h64 Host byte order 64-bit integer + */ +uint64_t csp_htole64(uint64_t h64); + +/** + * Convert 64-bit integer from big endian byte order to host byte order + * @param be64 Big endian byte order 64-bit integer + */ +uint64_t csp_betoh64(uint64_t be64); + +/** + * Convert 64-bit integer from little endian byte order to host byte order + * @param le64 Little endian byte order 64-bit integer + */ +uint64_t csp_letoh64(uint64_t le64); + +/** + * Convert float from host to network byte order + * @param f Float in host order + * @return Float in network order + */ +float csp_htonflt(float f); + +/** + * Convert float from network to host byte order + * @param f Float in network order + * @return Float in host order + */ +float csp_ntohflt(float f); + +/** + * Convert double from host to network byte order + * @param d Double in host order + * @return Double in network order + */ +double csp_htondbl(double d); + +/** + * Convert double from network to host order + * @param d Double in network order + * @return Double in host order + */ +double csp_ntohdbl(double d); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_ENDIAN_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_error.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_error.h new file mode 100644 index 00000000..31866607 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_error.h @@ -0,0 +1,50 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_ERROR_H_ +#define _CSP_ERROR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_ERR_NONE 0 /* No error */ +#define CSP_ERR_NOMEM -1 /* Not enough memory */ +#define CSP_ERR_INVAL -2 /* Invalid argument */ +#define CSP_ERR_TIMEDOUT -3 /* Operation timed out */ +#define CSP_ERR_USED -4 /* Resource already in use */ +#define CSP_ERR_NOTSUP -5 /* Operation not supported */ +#define CSP_ERR_BUSY -6 /* Device or resource busy */ +#define CSP_ERR_ALREADY -7 /* Connection already in progress */ +#define CSP_ERR_RESET -8 /* Connection reset */ +#define CSP_ERR_NOBUFS -9 /* No more buffer space available */ +#define CSP_ERR_TX -10 /* Transmission failed */ +#define CSP_ERR_DRIVER -11 /* Error in driver layer */ +#define CSP_ERR_AGAIN -12 /* Resource temporarily unavailable */ + +#define CSP_ERR_HMAC -100 /* HMAC failed */ +#define CSP_ERR_XTEA -101 /* XTEA failed */ +#define CSP_ERR_CRC32 -102 /* CRC32 failed */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_ERROR_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_iflist.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_iflist.h new file mode 100644 index 00000000..55875657 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_iflist.h @@ -0,0 +1,56 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_IFLIST_H_ +#define CSP_IFLIST_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Add interface to list + * @param ifc Pointer to interface to add + */ +void csp_iflist_add(csp_iface_t *ifc); + +/** + * Lookup interface by name + * @param name String with interface name + * @return Pointer to interface or NULL if not found + */ +csp_iface_t * csp_iflist_get_by_name(const char *name); + +/** + * Print list of interfaces to stdout + */ +void csp_iflist_print(void); + +/** + * Return list of registered interfaces. + */ +csp_iface_t * csp_iflist_get(void); + +#ifdef __cplusplus +} +#endif +#endif /* CSP_IFLIST_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_interface.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_interface.h new file mode 100644 index 00000000..8f04bee3 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_interface.h @@ -0,0 +1,54 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_INTERFACE_H_ +#define _CSP_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * Inputs a new packet into the system + * This function is called from interface drivers ISR to route and accept packets. + * But it can also be called from a task, provided that the pxTaskWoken parameter is NULL! + * + * EXTREMELY IMPORTANT: + * pxTaskWoken arg must ALWAYS be NULL if called from task, + * and ALWAYS be NON NULL if called from ISR! + * If this condition is met, this call is completely thread-safe + * + * This function is fire and forget, it returns void, meaning + * that a packet will always be either accepted or dropped + * so the memory will always be freed. + * + * @param packet A pointer to the incoming packet + * @param interface A pointer to the incoming interface TX function. + * @param pxTaskWoken This must be a pointer a valid variable if called from ISR or NULL otherwise! + */ +void csp_qfifo_write(csp_packet_t *packet, csp_iface_t *interface, CSP_BASE_TYPE *pxTaskWoken); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_INTERFACE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_platform.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_platform.h new file mode 100644 index 00000000..b33292e9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_platform.h @@ -0,0 +1,56 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_PLATFORM_H_ +#define _CSP_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Set OS */ +#if defined(CSP_POSIX) || defined(CSP_WINDOWS) || defined(CSP_MACOSX) + #define CSP_BASE_TYPE int + #define CSP_MAX_DELAY (UINT32_MAX) + #define CSP_INFINITY (UINT32_MAX) + #define CSP_DEFINE_CRITICAL(lock) static csp_bin_sem_handle_t lock + #define CSP_INIT_CRITICAL(lock) ({(csp_bin_sem_create(&lock) == CSP_SEMAPHORE_OK) ? CSP_ERR_NONE : CSP_ERR_NOMEM;}) + #define CSP_ENTER_CRITICAL(lock) do { csp_bin_sem_wait(&lock, CSP_MAX_DELAY); } while(0) + #define CSP_EXIT_CRITICAL(lock) do { csp_bin_sem_post(&lock); } while(0) +#elif defined(CSP_FREERTOS) + #include "FreeRTOS.h" + #define CSP_BASE_TYPE portBASE_TYPE + #define CSP_MAX_DELAY portMAX_DELAY + #define CSP_INFINITY portMAX_DELAY + #define CSP_DEFINE_CRITICAL(lock) + #define CSP_INIT_CRITICAL(lock) ({CSP_ERR_NONE;}) + #define CSP_ENTER_CRITICAL(lock) do { portENTER_CRITICAL(); } while (0) + #define CSP_EXIT_CRITICAL(lock) do { portEXIT_CRITICAL(); } while (0) +#else + #error "OS must be either CSP_POSIX, CSP_MACOSX, CSP_FREERTOS OR CSP_WINDOWS" +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_PLATFORM_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_rtable.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_rtable.h new file mode 100644 index 00000000..34cd18e2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_rtable.h @@ -0,0 +1,149 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_RTABLE_H_ +#define CSP_RTABLE_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define CSP_NODE_MAC 0xFF +#define CSP_ROUTE_COUNT (CSP_ID_HOST_MAX + 2) +#define CSP_ROUTE_TABLE_SIZE 5 * CSP_ROUTE_COUNT + +/** + * Find outgoing interface in routing table + * @param id Destination node + * @return pointer to outgoing interface or NULL + */ +csp_iface_t * csp_rtable_find_iface(uint8_t id); + +/** + * Find MAC address associated with node + * @param id Destination node + * @return MAC address + */ +uint8_t csp_rtable_find_mac(uint8_t id); + +/** + * Setup routing entry + * @param node Host + * @param mask Number of bits in netmask + * @param ifc Interface + * @param mac MAC address + * @return CSP error type + */ +int csp_rtable_set(uint8_t node, uint8_t mask, csp_iface_t *ifc, uint8_t mac); + +/** + * Print routing table to stdout + */ +void csp_rtable_print(void); + + +/** + * Load the routing table from a buffer + * (deprecated, please use new csp_rtable_load) + * + * Warning: + * The table will be RAW from memory and contains direct pointers, not interface names. + * Therefore it's very important that a saved routing table is deleted after a firmware update + * + * @param route_table_in pointer to routing table buffer + */ +void csp_route_table_load(uint8_t route_table_in[CSP_ROUTE_TABLE_SIZE]); + +/** + * Save the routing table to a buffer + * (deprecated, please use new csp_rtable_save) + * + * Warning: + * The table will be RAW from memory and contains direct pointers, not interface names. + * Therefore it's very important that a saved routing table is deleted after a firmware update + * + * @param route_table_out pointer to routing table buffer + */ +void csp_route_table_save(uint8_t route_table_out[CSP_ROUTE_TABLE_SIZE]); + +/** + * Save routing table as a string to a buffer, which can be parsed + * again by csp_rtable_load. + * @param buffer pointer to buffer + * @param maxlen length of buffer + * @return length of saved string + */ +int csp_rtable_save(char * buffer, int maxlen); + +/** + * Load routing table from a string in the format + * %u/%u %s %u + * - Address + * - Netmask + * - Ifname + * - Mac Address (this field is optional) + * An example routing string is "0/0 I2C, 8/2 KISS" + * The string must be \0 null terminated + * @param buffer Pointer to string + */ +void csp_rtable_load(const char * buffer); + +/** + * Check string for valid routing table + * @param buffer Pointer to string + * @return number of valid entries found + */ +int csp_rtable_check(const char * buffer); + +/** + * Clear routing table: + * This could be done before load to ensure an entire clean table is loaded. + */ +void csp_rtable_clear(void); + +/** + * Setup routing entry to single node + * (deprecated, please use csp_rtable_set) + * + * @param node Host + * @param ifc Interface + * @param mac MAC address + * @return CSP error type + */ +#define csp_route_set(node, ifc, mac) csp_rtable_set(node, CSP_ID_HOST_SIZE, ifc, mac) + +/** + * Print routing table + * (deprecated, please use csp_rtable_print) + */ +#define csp_route_print_table() csp_rtable_print(); + +/** + * Print list of interfaces + * (deprecated, please use csp_iflist_print) + */ +#define csp_route_print_interfaces() csp_iflist_print(); + +#ifdef __cplusplus +} +#endif +#endif /* CSP_RTABLE_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/csp_types.h b/gomspace/libgscsp/lib/libcsp/include/csp/csp_types.h new file mode 100644 index 00000000..a9cc28cd --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/csp_types.h @@ -0,0 +1,235 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_TYPES_H_ +#define CSP_TYPES_H_ + +#include +#include // -> CSP_HAVE_X defines +#ifdef CSP_HAVE_STDBOOL_H +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Make bool for compilers without stdbool.h */ +#ifndef CSP_HAVE_STDBOOL_H +#define bool int +#define false 0 +#define true !false +#endif + +/** + * RESERVED PORTS (SERVICES) + */ + +enum csp_reserved_ports_e { + CSP_CMP = 0, + CSP_PING = 1, + CSP_PS = 2, + CSP_MEMFREE = 3, + CSP_REBOOT = 4, + CSP_BUF_FREE = 5, + CSP_UPTIME = 6, + CSP_ANY = (CSP_MAX_BIND_PORT + 1), + CSP_PROMISC = (CSP_MAX_BIND_PORT + 2) +}; + +typedef enum { + CSP_PRIO_CRITICAL = 0, + CSP_PRIO_HIGH = 1, + CSP_PRIO_NORM = 2, + CSP_PRIO_LOW = 3, +} csp_prio_t; + +#define CSP_PRIORITIES (1 << CSP_ID_PRIO_SIZE) + +#ifdef CSP_USE_QOS +#define CSP_RX_QUEUE_LENGTH (CSP_CONN_QUEUE_LENGTH / CSP_PRIORITIES) +#define CSP_ROUTE_FIFOS CSP_PRIORITIES +#define CSP_RX_QUEUES CSP_PRIORITIES +#else +#define CSP_RX_QUEUE_LENGTH CSP_CONN_QUEUE_LENGTH +#define CSP_ROUTE_FIFOS 1 +#define CSP_RX_QUEUES 1 +#endif + +/** Size of bit-fields in CSP header */ +#define CSP_ID_PRIO_SIZE 2 +#define CSP_ID_HOST_SIZE 5 +#define CSP_ID_PORT_SIZE 6 +#define CSP_ID_FLAGS_SIZE 8 + +#define CSP_HEADER_BITS (CSP_ID_PRIO_SIZE + 2 * CSP_ID_HOST_SIZE + 2 * CSP_ID_PORT_SIZE + CSP_ID_FLAGS_SIZE) +#define CSP_HEADER_LENGTH (CSP_HEADER_BITS / 8) + +#if CSP_HEADER_BITS != 32 && __GNUC__ +#error "Header length must be 32 bits" +#endif + +/** Highest number to be entered in field */ +#define CSP_ID_PRIO_MAX ((1 << (CSP_ID_PRIO_SIZE)) - 1) +#define CSP_ID_HOST_MAX ((1 << (CSP_ID_HOST_SIZE)) - 1) +#define CSP_ID_PORT_MAX ((1 << (CSP_ID_PORT_SIZE)) - 1) +#define CSP_ID_FLAGS_MAX ((1 << (CSP_ID_FLAGS_SIZE)) - 1) + +/** Identifier field masks */ +#define CSP_ID_PRIO_MASK ((uint32_t) CSP_ID_PRIO_MAX << (CSP_ID_FLAGS_SIZE + 2 * CSP_ID_PORT_SIZE + 2 * CSP_ID_HOST_SIZE)) +#define CSP_ID_SRC_MASK ((uint32_t) CSP_ID_HOST_MAX << (CSP_ID_FLAGS_SIZE + 2 * CSP_ID_PORT_SIZE + 1 * CSP_ID_HOST_SIZE)) +#define CSP_ID_DST_MASK ((uint32_t) CSP_ID_HOST_MAX << (CSP_ID_FLAGS_SIZE + 2 * CSP_ID_PORT_SIZE)) +#define CSP_ID_DPORT_MASK ((uint32_t) CSP_ID_PORT_MAX << (CSP_ID_FLAGS_SIZE + 1 * CSP_ID_PORT_SIZE)) +#define CSP_ID_SPORT_MASK ((uint32_t) CSP_ID_PORT_MAX << (CSP_ID_FLAGS_SIZE)) +#define CSP_ID_FLAGS_MASK ((uint32_t) CSP_ID_FLAGS_MAX << (0)) + +#define CSP_ID_CONN_MASK (CSP_ID_SRC_MASK | CSP_ID_DST_MASK | CSP_ID_DPORT_MASK | CSP_ID_SPORT_MASK) + +/** + CSP identifier. +*/ +typedef union { + //! Entire identifier. + uint32_t ext; + //! Individual fields. + struct __attribute__((__packed__)) { +#if defined(CSP_BIG_ENDIAN) && !defined(CSP_LITTLE_ENDIAN) + unsigned int pri : CSP_ID_PRIO_SIZE; + unsigned int src : CSP_ID_HOST_SIZE; + unsigned int dst : CSP_ID_HOST_SIZE; + unsigned int dport : CSP_ID_PORT_SIZE; + unsigned int sport : CSP_ID_PORT_SIZE; + unsigned int flags : CSP_ID_FLAGS_SIZE; +#elif defined(CSP_LITTLE_ENDIAN) && !defined(CSP_BIG_ENDIAN) + unsigned int flags : CSP_ID_FLAGS_SIZE; + unsigned int sport : CSP_ID_PORT_SIZE; + unsigned int dport : CSP_ID_PORT_SIZE; + unsigned int dst : CSP_ID_HOST_SIZE; + unsigned int src : CSP_ID_HOST_SIZE; + unsigned int pri : CSP_ID_PRIO_SIZE; +#else +#error "Must define one of CSP_BIG_ENDIAN or CSP_LITTLE_ENDIAN in csp_platform.h" +#endif + }; +} csp_id_t; + +/** Broadcast address */ +#define CSP_BROADCAST_ADDR CSP_ID_HOST_MAX + +/** Default routing address */ +#define CSP_DEFAULT_ROUTE (CSP_ID_HOST_MAX + 1) + +/** CSP Flags */ +#define CSP_FRES1 0x80 // Reserved for future use +#define CSP_FRES2 0x40 // Reserved for future use +#define CSP_FRES3 0x20 // Reserved for future use +#define CSP_FFRAG 0x10 // Use fragmentation +#define CSP_FHMAC 0x08 // Use HMAC verification +#define CSP_FXTEA 0x04 // Use XTEA encryption +#define CSP_FRDP 0x02 // Use RDP protocol +#define CSP_FCRC32 0x01 // Use CRC32 checksum + +/** CSP Socket options */ +#define CSP_SO_NONE 0x0000 // No socket options +#define CSP_SO_RDPREQ 0x0001 // Require RDP +#define CSP_SO_RDPPROHIB 0x0002 // Prohibit RDP +#define CSP_SO_HMACREQ 0x0004 // Require HMAC +#define CSP_SO_HMACPROHIB 0x0008 // Prohibit HMAC +#define CSP_SO_XTEAREQ 0x0010 // Require XTEA +#define CSP_SO_XTEAPROHIB 0x0020 // Prohibit HMAC +#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_INTERNAL_LISTEN 0x1000 // Internal flag: listen called on socket + +/** CSP Connect options */ +#define CSP_O_NONE CSP_SO_NONE // No connection options +#define CSP_O_RDP CSP_SO_RDPREQ // Enable RDP +#define CSP_O_NORDP CSP_SO_RDPPROHIB // Disable RDP +#define CSP_O_HMAC CSP_SO_HMACREQ // Enable HMAC +#define CSP_O_NOHMAC CSP_SO_HMACPROHIB // Disable HMAC +#define CSP_O_XTEA CSP_SO_XTEAREQ // Enable XTEA +#define CSP_O_NOXTEA CSP_SO_XTEAPROHIB // Disable XTEA +#define CSP_O_CRC32 CSP_SO_CRC32REQ // Enable CRC32 +#define CSP_O_NOCRC32 CSP_SO_CRC32PROHIB // Disable CRC32 + +/** + * CSP PACKET STRUCTURE + * Note: This structure is constructed to fit + * with all interface frame types in order to + * have buffer reuse + */ +typedef struct __attribute__((__packed__)) { + uint8_t padding[CSP_PADDING_BYTES]; /**< Interface dependent padding */ + uint16_t length; /**< Length field must be just before CSP ID */ + csp_id_t id; /**< CSP id must be just before data */ + union { + uint8_t data[0]; /**< This just points to the rest of the buffer, without a size indication. */ + uint16_t data16[0]; /**< The data 16 and 32 types makes it easy to reference an integer (properly aligned) */ + uint32_t data32[0]; /**< without the compiler warning about strict aliasing rules. */ + }; +} csp_packet_t; + +/** Interface TX function */ +struct csp_iface_s; +typedef int (*nexthop_t)(struct csp_iface_s * interface, csp_packet_t *packet, uint32_t timeout); + +/** Interface struct */ +typedef struct csp_iface_s { + const char *name; /**< Interface name (keep below 10 bytes) */ + void * driver; /**< Pointer to interface handler structure */ + nexthop_t nexthop; /**< Next hop function */ + uint16_t mtu; /**< Maximum Transmission Unit of interface */ + uint8_t split_horizon_off; /**< Disable the route-loop prevention on if */ + uint32_t tx; /**< Successfully transmitted packets */ + uint32_t rx; /**< Successfully received packets */ + uint32_t tx_error; /**< Transmit errors */ + uint32_t rx_error; /**< Receive errors */ + uint32_t drop; /**< Dropped packets */ + uint32_t autherr; /**< Authentication errors */ + uint32_t frame; /**< Frame format errors */ + uint32_t txbytes; /**< Transmitted bytes */ + uint32_t rxbytes; /**< Received bytes */ + uint32_t irq; /**< Interrupts */ + struct csp_iface_s *next; /**< Next interface */ +} csp_iface_t; + +/** + * This define must be equal to the size of the packet overhead in csp_packet_t. + * It is used in csp_buffer_get() to check the allocated buffer size against + * the required buffer size. + */ +#define CSP_BUFFER_PACKET_OVERHEAD (sizeof(csp_packet_t) - sizeof(((csp_packet_t *)0)->data)) + +/** Forward declaration of socket and connection structures */ +typedef struct csp_conn_s csp_socket_t; +typedef struct csp_conn_s csp_conn_t; + +#define CSP_HOSTNAME_LEN 20 +#define CSP_MODEL_LEN 30 + +/* CSP_REBOOT magic values */ +#define CSP_REBOOT_MAGIC 0x80078007 +#define CSP_REBOOT_SHUTDOWN_MAGIC 0xD1E5529A + +#ifdef __cplusplus +} +#endif +#endif /* CSP_TYPES_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/drivers/can_socketcan.h b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/can_socketcan.h new file mode 100644 index 00000000..0963b414 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/can_socketcan.h @@ -0,0 +1,22 @@ +/* + * can_socketcan.h + * + * Created on: Feb 6, 2017 + * Author: johan + */ + +#ifndef LIB_CSP_INCLUDE_CSP_DRIVERS_CAN_SOCKETCAN_H_ +#define LIB_CSP_INCLUDE_CSP_DRIVERS_CAN_SOCKETCAN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +csp_iface_t * csp_can_socketcan_init(const char * ifc, int bitrate, int promisc); + +#ifdef __cplusplus +} +#endif +#endif /* LIB_CSP_INCLUDE_CSP_DRIVERS_CAN_SOCKETCAN_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/drivers/i2c.h b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/i2c.h new file mode 100644 index 00000000..3cf605ee --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/i2c.h @@ -0,0 +1,120 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file + * Common I2C interface, + * This file is derived from the Gomspace I2C driver, + * + */ + +#ifndef I2C_H_ +#define I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The return value of the driver is a bit strange, + * It should return E_NO_ERR if successfull and the value is -1 + */ +#define E_NO_ERR -1 + +/** + * Maximum transfer length on I2C + */ +#define I2C_MTU 256 + +/** + I2C device modes + @{ +*/ +/** + I2C Master mode. +*/ +#define I2C_MASTER 0 +/** + I2C Slave mode. +*/ +#define I2C_SLAVE 1 +/**@}*/ + +/** + Data structure for I2C frames. + This structs fits on top of #csp_packet_t, removing the need for copying data. +*/ +typedef struct __attribute__((packed)) i2c_frame_s { + //! Not used by CSP + uint8_t padding; + //! Not used by CSP - cleared before Tx + uint8_t retries; + //! Not used by CSP + uint32_t reserved; + //! Destination address + uint8_t dest; + //! Not used by CSP - cleared before Tx + uint8_t len_rx; + //! Length of \a data part + uint16_t len; + //! CSP data + uint8_t data[I2C_MTU]; +} i2c_frame_t; + +/** + Callback for receiving data. + + @param[in] frame received I2C frame + @param[out] pxTaskWoken can be set, if context switch is required due to received data. +*/ +typedef void (*i2c_callback_t) (i2c_frame_t * frame, void * pxTaskWoken); + +/** + Initialise the I2C driver + + Functions is called by csp_i2c_init(). + + @param handle Which I2C bus (if more than one exists) + @param mode I2C device mode. Must be either I2C_MASTER or I2C_SLAVE + @param addr Own slave address + @param speed Bus speed in kbps + @param queue_len_tx Length of transmit queue + @param queue_len_rx Length of receive queue + @param callback If this value is set, the driver will call this function instead of using an RX queue + @return Error code +*/ +int i2c_init(int handle, int mode, uint8_t addr, uint16_t speed, int queue_len_tx, int queue_len_rx, i2c_callback_t callback); + +/** + User I2C transmit function. + + Called by CSP, when sending message over I2C. + + @param handle Handle to the device + @param frame Pointer to I2C frame + @param timeout Ticks to wait + @return Error code +*/ +int i2c_send(int handle, i2c_frame_t * frame, uint16_t timeout); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/drivers/usart.h b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/usart.h new file mode 100644 index 00000000..d2da8448 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/drivers/usart.h @@ -0,0 +1,107 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** + * @file + * Common USART interface, + * This file is derived from the Gomspace USART driver, + * the main difference is the assumption that only one USART will be present on a PC + */ + +#ifndef USART_H_ +#define USART_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Usart configuration, to be used with the usart_init call. +*/ +struct usart_conf { + //! USART device. + const char *device; + //! bits per second. + uint32_t baudrate; + //! Number of data bits. + uint8_t databits; + //! Number of stop bits. + uint8_t stopbits; + //! Parity setting. + uint8_t paritysetting; + //! Enable parity checking (Windows only). + uint8_t checkparity; +}; + +/** + Initialise UART with the usart_conf data structure + @param[in] conf full configuration structure +*/ +void usart_init(struct usart_conf *conf); + +/** + In order to catch incoming chars use the callback. + Only one callback per interface. + @param[in] handle usart[0,1,2,3] + @param[in] callback function pointer +*/ +typedef void (*usart_callback_t) (uint8_t *buf, int len, void *pxTaskWoken); + +/** + Set callback for receiving data. +*/ +void usart_set_callback(usart_callback_t callback); + +/** + Insert a character to the RX buffer of a usart + + @param[in] c character to insert + @param[out] pxTaskWoken can be set, if context switch is required due to received data. +*/ +void usart_insert(char c, void *pxTaskWoken); + +/** + Polling putchar (stdin). + + @param[in] c Character to transmit +*/ +void usart_putc(char c); + +/** + Send char buffer on UART (stdout). + + @param[in] buf Pointer to data + @param[in] len Length of data +*/ +void usart_putstr(char *buf, int len); + +/** + Buffered getchar (stdin). + + @return Character received +*/ +char usart_getc(void); + +#ifdef __cplusplus +} +#endif +#endif /* USART_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_can.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_can.h new file mode 100644 index 00000000..229671f2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_can.h @@ -0,0 +1,76 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_CAN_H_ +#define _CSP_IF_CAN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/* CAN header macros */ +#define CFP_HOST_SIZE 5 +#define CFP_TYPE_SIZE 1 +#define CFP_REMAIN_SIZE 8 +#define CFP_ID_SIZE 10 + +/* Macros for extracting header fields */ +#define CFP_FIELD(id,rsiz,fsiz) ((uint32_t)((uint32_t)((id) >> (rsiz)) & (uint32_t)((1 << (fsiz)) - 1))) +#define CFP_SRC(id) CFP_FIELD(id, CFP_HOST_SIZE + CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE, CFP_HOST_SIZE) +#define CFP_DST(id) CFP_FIELD(id, CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE, CFP_HOST_SIZE) +#define CFP_TYPE(id) CFP_FIELD(id, CFP_REMAIN_SIZE + CFP_ID_SIZE, CFP_TYPE_SIZE) +#define CFP_REMAIN(id) CFP_FIELD(id, CFP_ID_SIZE, CFP_REMAIN_SIZE) +#define CFP_ID(id) CFP_FIELD(id, 0, CFP_ID_SIZE) + +/* Macros for building CFP headers */ +#define CFP_MAKE_FIELD(id,fsiz,rsiz) ((uint32_t)(((id) & (uint32_t)((uint32_t)(1 << (fsiz)) - 1)) << (rsiz))) +#define CFP_MAKE_SRC(id) CFP_MAKE_FIELD(id, CFP_HOST_SIZE, CFP_HOST_SIZE + CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE) +#define CFP_MAKE_DST(id) CFP_MAKE_FIELD(id, CFP_HOST_SIZE, CFP_TYPE_SIZE + CFP_REMAIN_SIZE + CFP_ID_SIZE) +#define CFP_MAKE_TYPE(id) CFP_MAKE_FIELD(id, CFP_TYPE_SIZE, CFP_REMAIN_SIZE + CFP_ID_SIZE) +#define CFP_MAKE_REMAIN(id) CFP_MAKE_FIELD(id, CFP_REMAIN_SIZE, CFP_ID_SIZE) +#define CFP_MAKE_ID(id) CFP_MAKE_FIELD(id, CFP_ID_SIZE, 0) + +/* Mask to uniquely separate connections */ +#define CFP_ID_CONN_MASK (CFP_MAKE_SRC((uint32_t)(1 << CFP_HOST_SIZE) - 1) | \ + CFP_MAKE_DST((uint32_t)(1 << CFP_HOST_SIZE) - 1) | \ + CFP_MAKE_ID((uint32_t)(1 << CFP_ID_SIZE) - 1)) + +/** + Default Maximum Transmission Unit (MTU) for CSP over CAN. + Maximum value is 2042 bytes. +*/ +#define CSP_CAN_MTU 256 + +int csp_can_rx(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc, CSP_BASE_TYPE *task_woken); +int csp_can_tx(csp_iface_t *interface, csp_packet_t *packet, uint32_t timeout); + +/* Must be implemented by the driver */ +int csp_can_tx_frame(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_IF_CAN_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_i2c.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_i2c.h new file mode 100644 index 00000000..c2169843 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_i2c.h @@ -0,0 +1,51 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_I2C_H_ +#define _CSP_IF_I2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include +#include + +extern csp_iface_t csp_if_i2c; + +/** + * Capture I2C RX events for CSP + * @param opt_addr local i2c address + * @param handle which i2c device to use + * @param speed interface speed in kHz (normally 100 or 400) + * @return csp_error.h code + */ +int csp_i2c_init(uint8_t opt_addr, int handle, int speed); + +void csp_i2c_rx(i2c_frame_t * frame, void * pxTaskWoken); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_IF_I2C_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_kiss.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_kiss.h new file mode 100644 index 00000000..f164cad1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_kiss.h @@ -0,0 +1,110 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_KISS_H_ +#define _CSP_IF_KISS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/** + * The KISS interface relies on the USART callback in order to parse incoming + * messaged from the serial interface. The USART callback however does not + * support passing the handle number of the responding USART, so you need to implement + * a USART callback for each handle and then call kiss_rx subsequently. + * + * In order to initialize the KISS interface. Fist call kiss_init() and then + * setup your usart to call csp_kiss_rx when new data is available. + * + * When a byte is not a part of a kiss packet, it will be returned to your + * usart driver using the usart_insert funtion that you provide. + * + * @param csp_iface pointer to interface + * @param buf pointer to incoming data + * @param len length of incoming data + * @param pxTaskWoken NULL if task context, pointer to variable if ISR + */ +void csp_kiss_rx(csp_iface_t * interface, uint8_t *buf, int len, void *pxTaskWoken); + +/** + * The putc function is used by the kiss interface to send + * a string of data to the serial port. This function must + * be implemented by the user, and passed to the kiss + * interface through the kiss_init function. + * @param buf byte to push + */ +typedef void (*csp_kiss_putc_f)(char buf); + +/** + * The characters not accepted by the kiss interface, are discarded + * using this function, which must be implemented by the user + * and passed through the kiss_init function. + * + * This reject function is typically used to display ASCII strings + * sent over the serial port, which are not in KISS format. Such as + * debugging information. + * + * @param c rejected character + * @param pxTaskWoken NULL if task context, pointer to variable if ISR + */ +typedef void (*csp_kiss_discard_f)(char c, void *pxTaskWoken); + +typedef enum { + KISS_MODE_NOT_STARTED, + KISS_MODE_STARTED, + KISS_MODE_ESCAPED, + KISS_MODE_SKIP_FRAME, +} kiss_mode_e; + +/** + * This structure should be statically allocated by the user + * and passed to the kiss interface during the init function + * no member information should be changed + */ +typedef struct csp_kiss_handle_s { + //! Put character on usart (tx). + csp_kiss_putc_f kiss_putc; + //! Discard - not KISS data (rx). + csp_kiss_discard_f kiss_discard; + //! Internal KISS state. + unsigned int rx_length; + //! Internal KISS state. + kiss_mode_e rx_mode; + //! Internal KISS state. + unsigned int rx_first; + //! Not used. + volatile unsigned char *rx_cbuf; + //! Internal KISS state. + csp_packet_t * rx_packet; +} csp_kiss_handle_t; + +void csp_kiss_init(csp_iface_t * csp_iface, csp_kiss_handle_t * csp_kiss_handle, csp_kiss_putc_f kiss_putc_f, csp_kiss_discard_f kiss_discard_f, const char * name); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_IF_KISS_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_lo.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_lo.h new file mode 100644 index 00000000..793c45ac --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_lo.h @@ -0,0 +1,38 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IF_LO_H_ +#define _CSP_IF_LO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* CSP includes */ +#include +#include + +extern csp_iface_t csp_if_lo; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_IF_LO_H_ diff --git a/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_zmqhub.h b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_zmqhub.h new file mode 100644 index 00000000..f9ab43bf --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/include/csp/interfaces/csp_if_zmqhub.h @@ -0,0 +1,26 @@ +#ifndef CSP_IF_ZMQHUB_H_ +#define CSP_IF_ZMQHUB_H_ + +#include + +extern csp_iface_t csp_if_zmqhub; + +/** + * Setup ZMQ interface + * @param addr only receive messages matching this address (255 means all) + * @param host Pointer to string containing zmqproxy host + * @return CSP_ERR + */ +int csp_zmqhub_init(uint8_t addr, const char * host); + +/** + * Setup ZMQ interface + * @param addr only receive messages matching this address (255 means all) + * @param publisher_endpoint Pointer to string containing zmqproxy publisher endpoint + * @param subscriber_endpoint Pointer to string containing zmqproxy subscriber endpoint + * @return CSP_ERR + */ +int csp_zmqhub_init_w_endpoints(uint8_t addr, const char * publisher_url, + const char * subscriber_url); + +#endif /* CSP_IF_ZMQHUB_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_malloc.c new file mode 100644 index 00000000..97e5c8f4 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_malloc.c @@ -0,0 +1,33 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* FreeRTOS includes */ +#include + +void * csp_malloc(size_t size) { + return pvPortMalloc(size); +} + +void csp_free(void *ptr) { + vPortFree(ptr); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_queue.c new file mode 100644 index 00000000..44efd0eb --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_queue.c @@ -0,0 +1,66 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* FreeRTOS includes */ +#include +#include + +/* CSP includes */ +#include + +#include + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return xQueueCreate(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + vQueueDelete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void * value, uint32_t timeout) { + if (timeout != CSP_MAX_DELAY) + timeout = timeout / portTICK_RATE_MS; + return xQueueSendToBack(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + return xQueueSendToBackFromISR(handle, value, task_woken); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void * buf, uint32_t timeout) { + if (timeout != CSP_MAX_DELAY) + timeout = timeout / portTICK_RATE_MS; + return xQueueReceive(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken) { + return xQueueReceiveFromISR(handle, buf, task_woken); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return uxQueueMessagesWaiting(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return uxQueueMessagesWaitingFromISR(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_semaphore.c new file mode 100644 index 00000000..b91757e5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_semaphore.c @@ -0,0 +1,96 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* FreeRTOS includes */ +#include +#include + +/* CSP includes */ +#include + +#include +#include +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + *mutex = xSemaphoreCreateMutex(); + if (*mutex) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + return csp_bin_sem_remove(mutex); +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + return csp_bin_sem_wait(mutex, timeout); +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + return csp_bin_sem_post(mutex); +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + vSemaphoreCreateBinary(*sem); + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + if ((sem != NULL) && (*sem != NULL)) { + csp_queue_remove(*sem); + } + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + csp_log_lock("Wait: %p", sem); + if (timeout != CSP_MAX_DELAY) + timeout = timeout / portTICK_RATE_MS; + if (xSemaphoreTake(*sem, timeout) == pdPASS) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + csp_log_lock("Post: %p", sem); + if (xSemaphoreGive(*sem) == pdPASS) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + csp_log_lock("Post: %p", sem); + if (xSemaphoreGiveFromISR(*sem, task_woken) == pdPASS) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_system.c new file mode 100644 index 00000000..a81c84b4 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_system.c @@ -0,0 +1,139 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { +#if (tskKERNEL_VERSION_MAJOR < 8) + vTaskList((signed portCHAR *) out); +#else + vTaskList(out); +#endif + return CSP_ERR_NONE; +} + +int csp_sys_tasklist_size(void) { + return 40 * uxTaskGetNumberOfTasks(); +} + +uint32_t csp_sys_memfree(void) { + + uint32_t total = 0, max = UINT32_MAX, size; + void * pmem; + + /* If size_t is less than 32 bits, start with 10 KiB */ + size = sizeof(uint32_t) > sizeof(size_t) ? 10000 : 1000000; + + while (1) { + pmem = pvPortMalloc(size + total); + if (pmem == NULL) { + max = size + total; + size = size / 2; + } else { + total += size; + if (total + size >= max) + size = size / 2; + vPortFree(pmem); + } + if (size < 32) break; + } + + return total; +} + +int csp_sys_reboot(void) { + + extern void __attribute__((weak)) cpu_set_reset_cause(unsigned int); + if (cpu_set_reset_cause) + cpu_set_reset_cause(1); + + extern void __attribute__((weak)) cpu_reset(void); + if (cpu_reset) { + cpu_reset(); + while (1); + } + + csp_log_error("Failed to reboot"); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { + + extern void __attribute__((weak)) cpu_shutdown(void); + if (cpu_shutdown) { + cpu_shutdown(); + while (1); + } + + csp_log_error("Failed to shutdown"); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + + unsigned int color_code, modifier_code; + switch (color & COLOR_MASK_COLOR) { + case COLOR_BLACK: + color_code = 30; break; + case COLOR_RED: + color_code = 31; break; + case COLOR_GREEN: + color_code = 32; break; + case COLOR_YELLOW: + color_code = 33; break; + case COLOR_BLUE: + color_code = 34; break; + case COLOR_MAGENTA: + color_code = 35; break; + case COLOR_CYAN: + color_code = 36; break; + case COLOR_WHITE: + color_code = 37; break; + case COLOR_RESET: + default: + color_code = 0; break; + } + + switch (color & COLOR_MASK_MODIFIER) { + case COLOR_BOLD: + modifier_code = 1; break; + case COLOR_UNDERLINE: + modifier_code = 2; break; + case COLOR_BLINK: + modifier_code = 3; break; + case COLOR_HIDE: + modifier_code = 4; break; + case COLOR_NORMAL: + default: + modifier_code = 0; break; + } + + printf("\033[%u;%um", modifier_code, color_code); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_thread.c new file mode 100644 index 00000000..af8296cd --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_thread.c @@ -0,0 +1,38 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { +#if (tskKERNEL_VERSION_MAJOR >= 8) + portBASE_TYPE ret = xTaskCreate(routine, thread_name, stack_depth, parameters, priority, handle); +#else + portBASE_TYPE ret = xTaskCreate(routine, (signed char *) thread_name, stack_depth, parameters, priority, handle); +#endif + if (ret != pdTRUE) + return CSP_ERR_NOMEM; + return CSP_ERR_NONE; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_time.c new file mode 100644 index 00000000..fd54a8cb --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/freertos/csp_time.c @@ -0,0 +1,46 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* FreeRTOS includes */ +#include +#include + +/* CSP includes */ +#include + +#include + +uint32_t csp_get_ms(void) { + return (uint32_t)(xTaskGetTickCount() * (1000/configTICK_RATE_HZ)); +} + +uint32_t csp_get_ms_isr(void) { + return (uint32_t)(xTaskGetTickCountFromISR() * (1000/configTICK_RATE_HZ)); +} + +uint32_t csp_get_s(void) { + return (uint32_t)(xTaskGetTickCount()/configTICK_RATE_HZ); +} + +uint32_t csp_get_s_isr(void) { + return (uint32_t)(xTaskGetTickCountFromISR()/configTICK_RATE_HZ); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_malloc.c new file mode 100644 index 00000000..95bb8cc7 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_malloc.c @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +void * csp_malloc(size_t size) { + return malloc(size); +} + +void csp_free(void *ptr) { + free(ptr); +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_queue.c new file mode 100644 index 00000000..a2fb1b4f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_queue.c @@ -0,0 +1,64 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include +#include + + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return pthread_queue_create(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + return pthread_queue_delete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout) { + return pthread_queue_enqueue(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + if (task_woken != NULL) + *task_woken = 0; + return csp_queue_enqueue(handle, value, 0); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout) { + return pthread_queue_dequeue(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void *buf, CSP_BASE_TYPE * task_woken) { + *task_woken = 0; + return csp_queue_dequeue(handle, buf, 0); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_semaphore.c new file mode 100644 index 00000000..915447f3 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_semaphore.c @@ -0,0 +1,105 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + csp_log_lock("Mutex init: %p", mutex); + *mutex = pthread_queue_create(1, sizeof(int)); + if (mutex) { + int dummy = 0; + pthread_queue_enqueue(*mutex, &dummy, 0); + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + pthread_queue_delete(*mutex); + return CSP_SEMAPHORE_OK; +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + + int ret; + csp_log_lock("Wait: %p timeout %"PRIu32, mutex, timeout); + + if (timeout == CSP_INFINITY) { + /* TODO: fix this to be infinite */ + int dummy = 0; + if (pthread_queue_dequeue(*mutex, &dummy, timeout) == PTHREAD_QUEUE_OK) + ret = CSP_MUTEX_OK; + else + ret = CSP_MUTEX_ERROR; + } else { + int dummy = 0; + if (pthread_queue_dequeue(*mutex, &dummy, timeout) == PTHREAD_QUEUE_OK) + ret = CSP_MUTEX_OK; + else + ret = CSP_MUTEX_ERROR; + } + + return ret == CSP_MUTEX_ERROR ? CSP_SEMAPHORE_ERROR : CSP_SEMAPHORE_OK; +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + int dummy = 0; + if (pthread_queue_enqueue(*mutex, &dummy, 0) == PTHREAD_QUEUE_OK) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + return csp_mutex_create(sem); +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + return csp_mutex_remove(sem); +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + return csp_mutex_lock(sem, timeout); +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + return csp_mutex_unlock(sem); +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + return csp_mutex_unlock(sem); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_system.c new file mode 100644 index 00000000..834cb210 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_system.c @@ -0,0 +1,99 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { + strcpy(out, "Tasklist not available on OSX"); + return CSP_ERR_NONE; +} + +int csp_sys_tasklist_size(void) { + return 100; +} + +uint32_t csp_sys_memfree(void) { + /* TODO: Fix memory free on OSX */ + uint32_t total = 0; + return total; +} + +int csp_sys_reboot(void) { + /* TODO: Fix reboot on OSX */ + csp_log_error("Failed to reboot"); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { + /* TODO: Fix shutdown on OSX */ + csp_log_error("Failed to shutdown"); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + + unsigned int color_code, modifier_code; + switch (color & COLOR_MASK_COLOR) { + case COLOR_BLACK: + color_code = 30; break; + case COLOR_RED: + color_code = 31; break; + case COLOR_GREEN: + color_code = 32; break; + case COLOR_YELLOW: + color_code = 33; break; + case COLOR_BLUE: + color_code = 34; break; + case COLOR_MAGENTA: + color_code = 35; break; + case COLOR_CYAN: + color_code = 36; break; + case COLOR_WHITE: + color_code = 37; break; + case COLOR_RESET: + default: + color_code = 0; break; + } + + switch (color & COLOR_MASK_MODIFIER) { + case COLOR_BOLD: + modifier_code = 1; break; + case COLOR_UNDERLINE: + modifier_code = 2; break; + case COLOR_BLINK: + modifier_code = 3; break; + case COLOR_HIDE: + modifier_code = 4; break; + case COLOR_NORMAL: + default: + modifier_code = 0; break; + } + + printf("\033[%u;%um", modifier_code, color_code); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_thread.c new file mode 100644 index 00000000..ed64856a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_thread.c @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { + return pthread_create(handle, NULL, routine, parameters); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_time.c new file mode 100644 index 00000000..a53f27e6 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/csp_time.c @@ -0,0 +1,65 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include + +#include + +uint32_t csp_get_ms(void) { + struct timespec ts; + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + return (uint32_t)(ts.tv_sec*1000+ts.tv_nsec/1000000); +} + +uint32_t csp_get_ms_isr(void) { + return csp_get_ms(); +} + +uint32_t csp_get_s(void) { + struct timespec ts; + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + return (uint32_t)ts.tv_sec; +} + +uint32_t csp_get_s_isr(void) { + return csp_get_s(); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/macosx/pthread_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/pthread_queue.c new file mode 100644 index 00000000..c4ac8c1d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/macosx/pthread_queue.c @@ -0,0 +1,179 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* +Inspired by c-pthread-queue by Matthew Dickinson +http://code.google.com/p/c-pthread-queue/ +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +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; + if (pthread_mutex_init(&(q->mutex), NULL) || pthread_cond_init(&(q->cond_full), NULL) || pthread_cond_init(&(q->cond_empty), NULL)) { + free(q->buffer); + free(q); + q = NULL; + } + } else { + free(q); + q = NULL; + } + } + + return q; + +} + +void pthread_queue_delete(pthread_queue_t * q) { + + if (q == NULL) + return; + + free(q->buffer); + free(q); + + return; + +} + +int pthread_queue_enqueue(pthread_queue_t * queue, void * value, uint32_t timeout) { + + int ret; + + /* Calculate timeout */ + struct timespec ts; + + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + uint32_t sec = timeout / 1000; + uint32_t nsec = (timeout - 1000 * sec) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec > 1000000000) + ts.tv_sec++; + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + while (queue->items == queue->size) { + ret = pthread_cond_timedwait(&(queue->cond_full), &(queue->mutex), &ts); + if (ret != 0) { + pthread_mutex_unlock(&(queue->mutex)); + return PTHREAD_QUEUE_FULL; + } + } + + /* Coby object from input buffer */ + memcpy(queue->buffer+(queue->in * queue->item_size), value, queue->item_size); + queue->items++; + queue->in = (queue->in + 1) % queue->size; + pthread_mutex_unlock(&(queue->mutex)); + + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_empty)); + + return PTHREAD_QUEUE_OK; + +} + +int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) { + + int ret; + + /* Calculate timeout */ + struct timespec ts; + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + ts.tv_sec = mts.tv_sec; + ts.tv_nsec = mts.tv_nsec; + + uint32_t sec = timeout / 1000; + uint32_t nsec = (timeout - 1000 * sec) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec > 1000000000) + ts.tv_sec++; + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + while (queue->items == 0) { + ret = pthread_cond_timedwait(&(queue->cond_empty), &(queue->mutex), &ts); + if (ret != 0) { + pthread_mutex_unlock(&(queue->mutex)); + return PTHREAD_QUEUE_EMPTY; + } + } + + /* Coby object to output buffer */ + memcpy(buf, queue->buffer+(queue->out * queue->item_size), queue->item_size); + queue->items--; + queue->out = (queue->out + 1) % queue->size; + pthread_mutex_unlock(&(queue->mutex)); + + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_full)); + + return PTHREAD_QUEUE_OK; + +} + +int pthread_queue_items(pthread_queue_t * queue) { + + pthread_mutex_lock(&(queue->mutex)); + int items = queue->items; + pthread_mutex_unlock(&(queue->mutex)); + + return items; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_malloc.c new file mode 100644 index 00000000..95bb8cc7 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_malloc.c @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +void * csp_malloc(size_t size) { + return malloc(size); +} + +void csp_free(void *ptr) { + free(ptr); +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_queue.c new file mode 100644 index 00000000..a2fb1b4f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_queue.c @@ -0,0 +1,64 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* CSP includes */ +#include + +#include +#include + + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return pthread_queue_create(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + return pthread_queue_delete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout) { + return pthread_queue_enqueue(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + if (task_woken != NULL) + *task_woken = 0; + return csp_queue_enqueue(handle, value, 0); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout) { + return pthread_queue_dequeue(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void *buf, CSP_BASE_TYPE * task_woken) { + *task_woken = 0; + return csp_queue_dequeue(handle, buf, 0); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return pthread_queue_items(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_semaphore.c new file mode 100644 index 00000000..6829dec2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_semaphore.c @@ -0,0 +1,164 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + csp_log_lock("Mutex init: %p", mutex); + if (pthread_mutex_init(mutex, NULL) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + if (pthread_mutex_destroy(mutex) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + + int ret; + struct timespec ts; + uint32_t sec, nsec; + + csp_log_lock("Wait: %p timeout %"PRIu32, mutex, timeout); + + if (timeout == CSP_INFINITY) { + ret = pthread_mutex_lock(mutex); + } else { + if (clock_gettime(CLOCK_REALTIME, &ts)) + return CSP_SEMAPHORE_ERROR; + + sec = timeout / 1000; + nsec = (timeout - 1000 * sec) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec >= 1000000000) + ts.tv_sec++; + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + ret = pthread_mutex_timedlock(mutex, &ts); + } + + if (ret != 0) + return CSP_SEMAPHORE_ERROR; + + return CSP_SEMAPHORE_OK; +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + if (pthread_mutex_unlock(mutex) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + csp_log_lock("Semaphore init: %p", sem); + if (sem_init(sem, 0, 1) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + if (sem_destroy(sem) == 0) + return CSP_SEMAPHORE_OK; + else + return CSP_SEMAPHORE_ERROR; +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + + int ret; + struct timespec ts; + uint32_t sec, nsec; + + csp_log_lock("Wait: %p timeout %"PRIu32, sem, timeout); + + if (timeout == CSP_INFINITY) { + ret = sem_wait(sem); + } else { + if (clock_gettime(CLOCK_REALTIME, &ts)) + return CSP_SEMAPHORE_ERROR; + + sec = timeout / 1000; + nsec = (timeout - 1000 * sec) * 1000000; + + ts.tv_sec += sec; + + if (ts.tv_nsec + nsec >= 1000000000) + ts.tv_sec++; + + ts.tv_nsec = (ts.tv_nsec + nsec) % 1000000000; + + ret = sem_timedwait(sem, &ts); + } + + if (ret != 0) + return CSP_SEMAPHORE_ERROR; + + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + CSP_BASE_TYPE dummy = 0; + return csp_bin_sem_post_isr(sem, &dummy); +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + csp_log_lock("Post: %p", sem); + *task_woken = 0; + + int value; + sem_getvalue(sem, &value); + if (value > 0) + return CSP_SEMAPHORE_OK; + + if (sem_post(sem) == 0) { + return CSP_SEMAPHORE_OK; + } else { + return CSP_SEMAPHORE_ERROR; + } +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_system.c new file mode 100644 index 00000000..6c882c7c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_system.c @@ -0,0 +1,131 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { + strcpy(out, "Tasklist not available on POSIX"); + return CSP_ERR_NONE; +} + +int csp_sys_tasklist_size(void) { + return 100; +} + +uint32_t csp_sys_memfree(void) { + uint32_t total = 0; + struct sysinfo info; + sysinfo(&info); + total = info.freeram * info.mem_unit; + return total; +} + +int csp_sys_reboot(void) { +#ifdef CSP_USE_INIT_SHUTDOWN + /* Let init(1) handle the reboot */ + int ret = system("reboot"); + (void) ret; /* Silence warning */ +#else + int magic = LINUX_REBOOT_CMD_RESTART; + + /* Sync filesystem before reboot */ + sync(); + reboot(magic); +#endif + + /* If reboot(2) returns, it is an error */ + csp_log_error("Failed to reboot: %s", strerror(errno)); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { +#ifdef CSP_USE_INIT_SHUTDOWN + /* Let init(1) handle the shutdown */ + int ret = system("halt"); + (void) ret; /* Silence warning */ +#else + int magic = LINUX_REBOOT_CMD_HALT; + + /* Sync filesystem before reboot */ + sync(); + reboot(magic); +#endif + + /* If reboot(2) returns, it is an error */ + csp_log_error("Failed to shutdown: %s", strerror(errno)); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + + unsigned int color_code, modifier_code; + switch (color & COLOR_MASK_COLOR) { + case COLOR_BLACK: + color_code = 30; break; + case COLOR_RED: + color_code = 31; break; + case COLOR_GREEN: + color_code = 32; break; + case COLOR_YELLOW: + color_code = 33; break; + case COLOR_BLUE: + color_code = 34; break; + case COLOR_MAGENTA: + color_code = 35; break; + case COLOR_CYAN: + color_code = 36; break; + case COLOR_WHITE: + color_code = 37; break; + case COLOR_RESET: + default: + color_code = 0; break; + } + + switch (color & COLOR_MASK_MODIFIER) { + case COLOR_BOLD: + modifier_code = 1; break; + case COLOR_UNDERLINE: + modifier_code = 2; break; + case COLOR_BLINK: + modifier_code = 3; break; + case COLOR_HIDE: + modifier_code = 4; break; + case COLOR_NORMAL: + default: + modifier_code = 0; break; + } + + printf("\033[%u;%um", modifier_code, color_code); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_thread.c new file mode 100644 index 00000000..3277d35d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_thread.c @@ -0,0 +1,55 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include + +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { + pthread_attr_t attributes, *attr_ref; + int return_code; + + if( pthread_attr_init(&attributes) == 0 ) + { + unsigned int stack_size = PTHREAD_STACK_MIN;// use at least one memory page + + while(stack_size < stack_depth)// must reach at least the provided size + { + stack_size += PTHREAD_STACK_MIN;// keep memory page boundary (some systems may fail otherwise)) + } + attr_ref = &attributes; + + pthread_attr_setdetachstate(attr_ref, PTHREAD_CREATE_DETACHED);// do not waste memory on each call + pthread_attr_setstacksize(attr_ref, stack_size); + } + else + { + attr_ref = NULL; + } + return_code = pthread_create(handle, attr_ref, routine, parameters); + if( attr_ref != NULL ) pthread_attr_destroy(attr_ref); + + return return_code; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_time.c new file mode 100644 index 00000000..c9677443 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/csp_time.c @@ -0,0 +1,54 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include + +#include + +uint32_t csp_get_ms(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (uint32_t)(ts.tv_sec*1000+ts.tv_nsec/1000000); + else + return 0; +} + +uint32_t csp_get_ms_isr(void) { + return csp_get_ms(); +} + +uint32_t csp_get_s(void) { + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return (uint32_t)ts.tv_sec; + else + return 0; +} + +uint32_t csp_get_s_isr(void) { + return csp_get_s(); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/posix/pthread_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/posix/pthread_queue.c new file mode 100644 index 00000000..e8b6d4ab --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/posix/pthread_queue.c @@ -0,0 +1,243 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* +Inspired by c-pthread-queue by Matthew Dickinson +http://code.google.com/p/c-pthread-queue/ +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* CSP includes */ +#include + +static inline int get_deadline(struct timespec *ts, uint32_t timeout_ms) +{ + int ret = clock_gettime(CLOCK_MONOTONIC, ts); + + if (ret < 0) { + return ret; + } + + uint32_t sec = timeout_ms / 1000; + uint32_t nsec = (timeout_ms - 1000 * sec) * 1000000; + + ts->tv_sec += sec; + + if (ts->tv_nsec + nsec >= 1000000000) { + ts->tv_sec++; + } + + ts->tv_nsec = (ts->tv_nsec + nsec) % 1000000000; + + return ret; +} + +static inline int init_cond_clock_monotonic(pthread_cond_t * cond) +{ + + int ret; + pthread_condattr_t attr; + + pthread_condattr_init(&attr); + ret = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + + if (ret == 0) { + ret = pthread_cond_init(cond, &attr); + } + + pthread_condattr_destroy(&attr); + return ret; + +} + +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; + if (pthread_mutex_init(&(q->mutex), NULL) || init_cond_clock_monotonic(&(q->cond_full)) || init_cond_clock_monotonic(&(q->cond_empty))) { + free(q->buffer); + free(q); + q = NULL; + } + } else { + free(q); + q = NULL; + } + } + + return q; + +} + +void pthread_queue_delete(pthread_queue_t * q) { + + if (q == NULL) + return; + + free(q->buffer); + free(q); + + return; + +} + + +static inline int wait_slot_available(pthread_queue_t * queue, struct timespec *ts) { + + int ret; + + while (queue->items == queue->size) { + + if (ts != NULL) { + ret = pthread_cond_timedwait(&(queue->cond_full), &(queue->mutex), ts); + } else { + ret = pthread_cond_wait(&(queue->cond_full), &(queue->mutex)); + } + + if (ret != 0 && errno != EINTR) { + return PTHREAD_QUEUE_FULL; //Timeout + } + } + + return PTHREAD_QUEUE_OK; + +} + +int pthread_queue_enqueue(pthread_queue_t * queue, void * value, uint32_t timeout) { + + int ret; + struct timespec ts; + struct timespec *pts = NULL; + + /* Calculate timeout */ + if (timeout != CSP_MAX_DELAY) { + if (get_deadline(&ts, timeout) != 0) { + return PTHREAD_QUEUE_ERROR; + } + pts = &ts; + } else { + pts = NULL; + } + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + + ret = wait_slot_available(queue, pts); + if (ret == PTHREAD_QUEUE_OK) { + /* Copy object from input buffer */ + memcpy(queue->buffer+(queue->in * queue->item_size), value, queue->item_size); + queue->items++; + queue->in = (queue->in + 1) % queue->size; + } + + pthread_mutex_unlock(&(queue->mutex)); + + if (ret == PTHREAD_QUEUE_OK) { + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_empty)); + } + + return ret; + +} + +static inline int wait_item_available(pthread_queue_t * queue, struct timespec *ts) { + + int ret; + + while (queue->items == 0) { + + if (ts != NULL) { + ret = pthread_cond_timedwait(&(queue->cond_empty), &(queue->mutex), ts); + } else { + ret = pthread_cond_wait(&(queue->cond_empty), &(queue->mutex)); + } + + if (ret != 0 && errno != EINTR) { + return PTHREAD_QUEUE_EMPTY; //Timeout + } + } + + return PTHREAD_QUEUE_OK; + +} + +int pthread_queue_dequeue(pthread_queue_t * queue, void * buf, uint32_t timeout) { + + int ret; + struct timespec ts; + struct timespec *pts; + + /* Calculate timeout */ + if (timeout != CSP_MAX_DELAY) { + if (get_deadline(&ts, timeout) != 0) { + return PTHREAD_QUEUE_ERROR; + } + pts = &ts; + } else { + pts = NULL; + } + + /* Get queue lock */ + pthread_mutex_lock(&(queue->mutex)); + + ret = wait_item_available(queue, pts); + if (ret == PTHREAD_QUEUE_OK) { + /* Coby object to output buffer */ + memcpy(buf, queue->buffer+(queue->out * queue->item_size), queue->item_size); + queue->items--; + queue->out = (queue->out + 1) % queue->size; + } + + pthread_mutex_unlock(&(queue->mutex)); + + if (ret == PTHREAD_QUEUE_OK) { + /* Nofify blocked threads */ + pthread_cond_broadcast(&(queue->cond_full)); + } + + return ret; + +} + +int pthread_queue_items(pthread_queue_t * queue) { + + pthread_mutex_lock(&(queue->mutex)); + int items = queue->items; + pthread_mutex_unlock(&(queue->mutex)); + + return items; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/README b/gomspace/libgscsp/lib/libcsp/src/arch/windows/README new file mode 100644 index 00000000..b97ce7f5 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/README @@ -0,0 +1,18 @@ +This directory contains files specific to the windows port of libcsp. + +To compile and create a static library, execute: + + python waf configure --with-os=windows build + +from the root of this project. Note python must be in PATH. + +The build requirements are: + * Windows Vista SP1 + * A recent version of MinGW _or_ MinGW-w64 + * Windows API headers + * cPython 2.5 or newer + +What provides the Windows API headers depends on the development environment: +Using MinGW: Headers provided by w32api package. windows_glue.h header is needed because these headers do not declare condition variables. +Using MinGW-w64: Headers should be available in the default configuration. You may have to compile the distribution from source. windows_glue.h should not be needed. + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_malloc.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_malloc.c new file mode 100644 index 00000000..4b301e49 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_malloc.c @@ -0,0 +1,9 @@ +#include + +void * csp_malloc(size_t size) { + return malloc(size); +} + +void csp_free(void * ptr) { + free(ptr); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_queue.c new file mode 100644 index 00000000..177f8fa9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_queue.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include "windows_queue.h" + +csp_queue_handle_t csp_queue_create(int length, size_t item_size) { + return windows_queue_create(length, item_size); +} + +void csp_queue_remove(csp_queue_handle_t queue) { + windows_queue_delete(queue); +} + +int csp_queue_enqueue(csp_queue_handle_t handle, void *value, uint32_t timeout) { + return windows_queue_enqueue(handle, value, timeout); +} + +int csp_queue_enqueue_isr(csp_queue_handle_t handle, void * value, CSP_BASE_TYPE * task_woken) { + if( task_woken != NULL ) + *task_woken = 0; + return windows_queue_enqueue(handle, value, 0); +} + +int csp_queue_dequeue(csp_queue_handle_t handle, void *buf, uint32_t timeout) { + return windows_queue_dequeue(handle, buf, timeout); +} + +int csp_queue_dequeue_isr(csp_queue_handle_t handle, void * buf, CSP_BASE_TYPE * task_woken) { + if( task_woken != NULL ) + *task_woken = 0; + return windows_queue_dequeue(handle, buf, 0); +} + +int csp_queue_size(csp_queue_handle_t handle) { + return windows_queue_items(handle); +} + +int csp_queue_size_isr(csp_queue_handle_t handle) { + return windows_queue_items(handle); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_semaphore.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_semaphore.c new file mode 100644 index 00000000..aa69251e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_semaphore.c @@ -0,0 +1,74 @@ +#include +#include +#include + +int csp_mutex_create(csp_mutex_t * mutex) { + HANDLE mutexHandle = CreateMutex(NULL, FALSE, FALSE); + if( mutexHandle == NULL ) { + return CSP_MUTEX_ERROR; + } + *mutex = mutexHandle; + return CSP_MUTEX_OK; +} + +int csp_mutex_remove(csp_mutex_t * mutex) { + if( !CloseHandle(*mutex) ) { + return CSP_MUTEX_ERROR; + } + return CSP_MUTEX_OK; +} + +int csp_mutex_lock(csp_mutex_t * mutex, uint32_t timeout) { + if(WaitForSingleObject(*mutex, timeout) == WAIT_OBJECT_0) { + return CSP_MUTEX_OK; + } + return CSP_MUTEX_ERROR; + +} + +int csp_mutex_unlock(csp_mutex_t * mutex) { + if( !ReleaseMutex(*mutex) ) { + return CSP_MUTEX_ERROR; + } + return CSP_MUTEX_OK; +} + +int csp_bin_sem_create(csp_bin_sem_handle_t * sem) { + HANDLE semHandle = CreateSemaphore(NULL, 1, 1, NULL); + if( semHandle == NULL ) { + return CSP_SEMAPHORE_ERROR; + } + *sem = semHandle; + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_remove(csp_bin_sem_handle_t * sem) { + if( !CloseHandle(*sem) ) { + return CSP_SEMAPHORE_ERROR; + } + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_wait(csp_bin_sem_handle_t * sem, uint32_t timeout) { + if( WaitForSingleObject(*sem, timeout) == WAIT_OBJECT_0 ) { + return CSP_SEMAPHORE_OK; + } + return CSP_SEMAPHORE_ERROR; + +} + +int csp_bin_sem_post(csp_bin_sem_handle_t * sem) { + if( !ReleaseSemaphore(*sem, 1, NULL) ) { + return CSP_SEMAPHORE_ERROR; + } + return CSP_SEMAPHORE_OK; +} + +int csp_bin_sem_post_isr(csp_bin_sem_handle_t * sem, CSP_BASE_TYPE * task_woken) { + if( task_woken != NULL ) { + *task_woken = 0; + } + return csp_bin_sem_post(sem); +} + + diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_system.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_system.c new file mode 100644 index 00000000..262c2052 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_system.c @@ -0,0 +1,60 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include + +int csp_sys_tasklist(char * out) { + strcpy(out, "Tasklist not available on Windows"); + return CSP_ERR_NONE; +} + +uint32_t csp_sys_memfree(void) { + MEMORYSTATUSEX statex; + statex.dwLength = sizeof(statex); + GlobalMemoryStatusEx(&statex); + DWORDLONG freePhysicalMem = statex.ullAvailPhys; + size_t total = (size_t) freePhysicalMem; + return (uint32_t)total; +} + +int csp_sys_reboot(void) { + /* TODO: Fix reboot on Windows */ + csp_log_error("Failed to reboot"); + + return CSP_ERR_INVAL; +} + +int csp_sys_shutdown(void) { + /* TODO: Fix shutdown on Windows */ + csp_log_error("Failed to shutdown"); + + return CSP_ERR_INVAL; +} + +void csp_sys_set_color(csp_color_t color) { + /* TODO: Add Windows color output here */ +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_thread.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_thread.c new file mode 100644 index 00000000..ef46a948 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_thread.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int csp_thread_create(csp_thread_return_t (* routine)(void *)__attribute__((stdcall)), const char * const thread_name, unsigned short stack_depth, void * parameters, unsigned int priority, csp_thread_handle_t * handle) { + HANDLE taskHandle = (HANDLE) _beginthreadex(NULL, stack_depth, routine, parameters, 0, 0); + if( taskHandle == 0 ) + return CSP_ERR_NOMEM; // Failure + *handle = taskHandle; + return CSP_ERR_NONE; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_time.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_time.c new file mode 100644 index 00000000..618292ab --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/csp_time.c @@ -0,0 +1,20 @@ +#include +#include +#include + +uint32_t csp_get_ms(void) { + return (uint32_t)GetTickCount(); +} + +uint32_t csp_get_ms_isr(void) { + return csp_get_ms(); +} + +uint32_t csp_get_s(void) { + uint32_t time_ms = csp_get_ms(); + return time_ms/1000; +} + +uint32_t csp_get_s_isr(void) { + return csp_get_s(); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_glue.h b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_glue.h new file mode 100644 index 00000000..6e0cf6db --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_glue.h @@ -0,0 +1,23 @@ +#ifndef WINDOWS_GLUE_H +#define WINDOWS_GLUE_H + +#include +#undef interface + +#if (_WIN32_WINNT >= 0x0600) + +#define RTL_CONDITION_VARIABLE_INIT 0 +#define RTL_CONDITION_VARIABLE_LOCKMODE_SHARED 1 +#define CONDITION_VARIABLE_INIT RTL_CONDITION_VARIABLE_INIT +#define CONDITION_VARIABLE_LOCKMODE_SHARED RTL_CONDITION_VARIABLE_LOCKMODE_SHARED + +typedef PVOID RTL_CONDITION_VARIABLE; +typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE; + +WINBASEAPI VOID WINAPI InitializeConditionVariable(PCONDITION_VARIABLE ConditionVariable); +WINBASEAPI WINBOOL WINAPI SleepConditionVariableCS(PCONDITION_VARIABLE ConditionVariable, PCRITICAL_SECTION CriticalSection, DWORD dwMilliseconds); +WINBASEAPI VOID WINAPI WakeAllConditionVariable(PCONDITION_VARIABLE ConditionVariable); +WINBASEAPI VOID WINAPI WakeConditionVariable(PCONDITION_VARIABLE ConditionVariable); + +#endif // _WIN#"_WINNT +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.c b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.c new file mode 100644 index 00000000..aa337dc8 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.c @@ -0,0 +1,91 @@ +#include "windows_queue.h" +#include "windows_glue.h" +#include + +static int queueFull(windows_queue_t * queue) { + return queue->items == queue->size; +} + +static int queueEmpty(windows_queue_t * queue) { + return queue->items == 0; +} + +windows_queue_t * windows_queue_create(int length, size_t item_size) { + windows_queue_t *queue = (windows_queue_t*)malloc(sizeof(windows_queue_t)); + if(queue == NULL) + goto queue_malloc_failed; + + queue->buffer = malloc(length*item_size); + if(queue->buffer == NULL) + goto buffer_malloc_failed; + + queue->size = length; + queue->item_size = item_size; + queue->items = 0; + queue->head_idx = 0; + + InitializeCriticalSection(&(queue->mutex)); + InitializeConditionVariable(&(queue->cond_full)); + InitializeConditionVariable(&(queue->cond_empty)); + goto queue_init_success; + +buffer_malloc_failed: + free(queue); + queue = NULL; +queue_malloc_failed: +queue_init_success: + return queue; +} + +void windows_queue_delete(windows_queue_t * q) { + if(q==NULL) return; + DeleteCriticalSection(&(q->mutex)); + free(q->buffer); + free(q); +} + +int windows_queue_enqueue(windows_queue_t * queue, void * value, int timeout) { + int offset; + EnterCriticalSection(&(queue->mutex)); + while(queueFull(queue)) { + int ret = SleepConditionVariableCS(&(queue->cond_full), &(queue->mutex), timeout); + if( !ret ) { + LeaveCriticalSection(&(queue->mutex)); + return ret == WAIT_TIMEOUT ? WINDOWS_QUEUE_FULL : WINDOWS_QUEUE_ERROR; + } + } + offset = ((queue->head_idx+queue->items) % queue->size) * queue->item_size; + memcpy((unsigned char*)queue->buffer + offset, value, queue->item_size); + queue->items++; + + LeaveCriticalSection(&(queue->mutex)); + WakeAllConditionVariable(&(queue->cond_empty)); + return WINDOWS_QUEUE_OK; +} + +int windows_queue_dequeue(windows_queue_t * queue, void * buf, int timeout) { + EnterCriticalSection(&(queue->mutex)); + while(queueEmpty(queue)) { + int ret = SleepConditionVariableCS(&(queue->cond_empty), &(queue->mutex), timeout); + if( !ret ) { + LeaveCriticalSection(&(queue->mutex)); + return ret == WAIT_TIMEOUT ? WINDOWS_QUEUE_EMPTY : WINDOWS_QUEUE_ERROR; + } + } + memcpy(buf, (unsigned char*)queue->buffer+(queue->head_idx%queue->size*queue->item_size), queue->item_size); + queue->items--; + queue->head_idx = (queue->head_idx + 1) % queue->size; + + LeaveCriticalSection(&(queue->mutex)); + WakeAllConditionVariable(&(queue->cond_full)); + return WINDOWS_QUEUE_OK; +} + +int windows_queue_items(windows_queue_t * queue) { + int items; + EnterCriticalSection(&(queue->mutex)); + items = queue->items; + LeaveCriticalSection(&(queue->mutex)); + + return items; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.h b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.h new file mode 100644 index 00000000..e6bc5423 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/arch/windows/windows_queue.h @@ -0,0 +1,41 @@ +#ifndef _WINDOWS_QUEUE_H_ +#define _WINDOWS_QUEUE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "windows_glue.h" +#undef interface + +#include + +#define WINDOWS_QUEUE_ERROR CSP_QUEUE_ERROR +#define WINDOWS_QUEUE_EMPTY CSP_QUEUE_ERROR +#define WINDOWS_QUEUE_FULL CSP_QUEUE_ERROR +#define WINDOWS_QUEUE_OK CSP_QUEUE_OK + +typedef struct windows_queue_s { + void * buffer; + int size; + int item_size; + int items; + int head_idx; + CRITICAL_SECTION mutex; + CONDITION_VARIABLE cond_full; + CONDITION_VARIABLE cond_empty; +} windows_queue_t; + +windows_queue_t * windows_queue_create(int length, size_t item_size); +void windows_queue_delete(windows_queue_t * q); +int windows_queue_enqueue(windows_queue_t * queue, void * value, int timeout); +int windows_queue_dequeue(windows_queue_t * queue, void * buf, int timeout); +int windows_queue_items(windows_queue_t * queue); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _WINDOWS_QUEUE_H_ + diff --git a/gomspace/libgscsp/lib/libcsp/src/bindings/python/pycsp.c b/gomspace/libgscsp/lib/libcsp/src/bindings/python/pycsp.c new file mode 100644 index 00000000..f1009d1a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/bindings/python/pycsp.c @@ -0,0 +1,1052 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if PY_MAJOR_VERSION == 3 +#define IS_PY3 +#endif + +static int is_capsule_of_type(PyObject* capsule, const char* expected_type) { + const char* capsule_name = PyCapsule_GetName(capsule); + if (strcmp(capsule_name, expected_type) != 0) { + PyErr_Format( + PyExc_TypeError, + "capsule contains unexpected type, expected=%s, got=%s", + expected_type, capsule_name); // TypeError is thrown + return 0; + } + return 1; +} + +/** + * csp/csp.h + */ + +/* + * void csp_service_handler(csp_conn_t *conn, csp_packet_t *packet); + */ +static PyObject* pycsp_service_handler(PyObject *self, PyObject *args) { + PyObject* conn_capsule; + PyObject* packet_capsule; + if (!PyArg_ParseTuple(args, "OO", &conn_capsule, &packet_capsule)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(conn_capsule, "csp_conn_t") || + !is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_service_handler( + (csp_conn_t*)PyCapsule_GetPointer(conn_capsule, "csp_conn_t"), + (csp_packet_t*)PyCapsule_GetPointer(packet_capsule, "csp_packet_t")); + + Py_RETURN_NONE; +} + +/* + * int csp_init(uint8_t my_node_address); + */ +static PyObject* pycsp_init(PyObject *self, PyObject *args) { + uint8_t my_node_address; + if (!PyArg_ParseTuple(args, "b", &my_node_address)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_init(my_node_address)); +} + +/* + * void csp_set_hostname(const char *hostname); + */ +static PyObject* pycsp_set_hostname(PyObject *self, PyObject *args) { + char* hostname; + if (!PyArg_ParseTuple(args, "s", &hostname)) { + return NULL; // TypeError is thrown + } + + csp_set_hostname(hostname); + Py_RETURN_NONE; +} + +/* + * const char *csp_get_hostname(void); + */ +static PyObject* pycsp_get_hostname(PyObject *self, PyObject *args) { + return Py_BuildValue("s", csp_get_hostname()); +} + +/* + * void csp_set_model(const char *model); + */ +static PyObject* pycsp_set_model(PyObject *self, PyObject *args) { + char* model; + if (!PyArg_ParseTuple(args, "s", &model)) { + return NULL; // TypeError is thrown + } + + csp_set_model(model); + Py_RETURN_NONE; +} + +/* + * const char *csp_get_model(void); + */ +static PyObject* pycsp_get_model(PyObject *self, PyObject *args) { + return Py_BuildValue("s", csp_get_model()); +} + +/* + * void csp_set_revision(const char *revision); + */ +static PyObject* pycsp_set_revision(PyObject *self, PyObject *args) { + char* revision; + if (!PyArg_ParseTuple(args, "s", &revision)) { + return NULL; // TypeError is thrown + } + + csp_set_revision(revision); + Py_RETURN_NONE; +} + +/* + * const char *csp_get_revision(void); + */ +static PyObject* pycsp_get_revision(PyObject *self, PyObject *args) { + return Py_BuildValue("s", csp_get_revision()); +} + +/* + * csp_socket_t *csp_socket(uint32_t opts); + */ +static PyObject* pycsp_socket(PyObject *self, PyObject *args) { + uint32_t opts = CSP_SO_NONE; + if (!PyArg_ParseTuple(args, "|I", &opts)) { + return NULL; // TypeError is thrown + } + + return PyCapsule_New(csp_socket(opts), "csp_socket_t", NULL); +} + +/* + * csp_conn_t *csp_accept(csp_socket_t *socket, uint32_t timeout); + */ +static PyObject* pycsp_accept(PyObject *self, PyObject *args) { + PyObject* socket_capsule; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "O|I", &socket_capsule, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(socket_capsule, "csp_socket_t")) { + return NULL; // TypeError is thrown + } + + void* socket = PyCapsule_GetPointer(socket_capsule, "csp_socket_t"); + csp_conn_t* conn = csp_accept((csp_socket_t*)socket, timeout); + if (conn == NULL) { + Py_RETURN_NONE; // because a capsule cannot contain a NULL-pointer + } + + return PyCapsule_New(conn, "csp_conn_t", NULL); +} + +/* + * csp_packet_t *csp_read(csp_conn_t *conn, uint32_t timeout); + */ +static PyObject* pycsp_read(PyObject *self, PyObject *args) { + PyObject* conn_capsule; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "O|I", &conn_capsule, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + csp_packet_t* packet = csp_read((csp_conn_t*)conn, timeout); + if (packet == NULL) { + Py_RETURN_NONE; // because capsule cannot contain a NULL-pointer + } + + return PyCapsule_New(packet, "csp_packet_t", NULL); +} + +/* +* int csp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) +*/ +static PyObject* pycsp_send(PyObject *self, PyObject *args) { + PyObject* conn_capsule; + PyObject* packet_capsule; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "OO|I", &conn_capsule, &packet_capsule, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + if (packet == NULL) { + Py_RETURN_NONE; + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + + int result = csp_send(conn, packet, timeout); + + return Py_BuildValue("i", result); +} + +/* + * int csp_transaction(uint8_t prio, uint8_t dest, uint8_t port, + * uint32_t timeout, void *outbuf, int outlen, + * void *inbuf, int inlen); + */ +static PyObject* pycsp_transaction(PyObject *self, PyObject *args) { + uint8_t prio; + uint8_t dest; + uint8_t port; + uint32_t timeout; + Py_buffer inbuf; + Py_buffer outbuf; + if (!PyArg_ParseTuple(args, "bbbIw*w*", &prio, &dest, &port, &timeout, &outbuf, &inbuf)) { + return NULL; // TypeError is thrown + } + + int result = csp_transaction(prio, dest, port, timeout, + outbuf.buf, outbuf.len, + inbuf.buf, inbuf.len); + + return Py_BuildValue("i", result); +} + +/* int csp_sendto(uint8_t prio, uint8_t dest, uint8_t dport, uint8_t src_port, uint32_t opts, csp_packet_t *packet, uint32_t timeout); */ +static PyObject* pycsp_sendto(PyObject *self, PyObject *args) { + uint8_t prio; + uint8_t dest; + uint8_t dport; + uint8_t src_port; + uint32_t opts; + PyObject* packet_capsule; + uint32_t timeout; + if (!PyArg_ParseTuple(args, "bbbbIOI", &prio, &dest, &dport, &src_port, &opts, &packet_capsule, &timeout)) { + Py_RETURN_NONE; + } + + void* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + if (packet == NULL) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", csp_sendto(prio, + dest, + dport, + src_port, + opts, + (csp_packet_t*)packet, + timeout)); +} + + +/* + * int csp_sendto_reply(csp_packet_t * request_packet, + * csp_packet_t * reply_packet, + * uint32_t opts, uint32_t timeout); + */ +static PyObject* pycsp_sendto_reply(PyObject *self, PyObject *args) { + PyObject* request_packet_capsule; + PyObject* reply_packet_capsule; + uint32_t opts = CSP_O_NONE; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "OO|II", &request_packet_capsule, &reply_packet_capsule, &opts, &timeout)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(request_packet_capsule, "csp_packet_t") || + !is_capsule_of_type(reply_packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + void* request_packet = PyCapsule_GetPointer(request_packet_capsule, "csp_packet_t"); + void* reply_packet = PyCapsule_GetPointer(reply_packet_capsule, "csp_packet_t"); + + return Py_BuildValue("i", csp_sendto_reply((csp_packet_t*)request_packet, + (csp_packet_t*)reply_packet, + opts, + timeout)); +} + +/* + * csp_conn_t *csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts); + */ +static PyObject* pycsp_connect(PyObject *self, PyObject *args) { + uint8_t prio; + uint8_t dest; + uint8_t dport; + uint32_t timeout; + uint32_t opts; + if (!PyArg_ParseTuple(args, "bbbII", &prio, &dest, &dport, &timeout, &opts)) { + return NULL; // TypeError is thrown + } + + csp_conn_t *conn = csp_connect(prio, dest, dport, timeout,opts); + + return PyCapsule_New(conn, "csp_conn_t", NULL); +} + +/* + * int csp_close(csp_conn_t *conn); + */ +static PyObject* pycsp_close(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void *conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_close((csp_conn_t*)conn)); +} + +/* + * int csp_conn_dport(csp_conn_t *conn); + */ +static PyObject* pycsp_conn_dport(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_dport((csp_conn_t*)conn)); +} + +/* + * int csp_conn_sport(csp_conn_t *conn); + */ +static PyObject* pycsp_conn_sport(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_sport((csp_conn_t*)conn)); +} + +/* int csp_conn_dst(csp_conn_t *conn); */ +static PyObject* pycsp_conn_dst(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_dst((csp_conn_t*)conn)); +} + +/* + * int csp_conn_src(csp_conn_t *conn); + */ +static PyObject* pycsp_conn_src(PyObject *self, PyObject *conn_capsule) { + if (!is_capsule_of_type(conn_capsule, "csp_conn_t")) { + return NULL; // TypeError is thrown + } + + void* conn = PyCapsule_GetPointer(conn_capsule, "csp_conn_t"); + return Py_BuildValue("i", csp_conn_src((csp_conn_t*)conn)); +} + +/* int csp_listen(csp_socket_t *socket, size_t conn_queue_length); */ +static PyObject* pycsp_listen(PyObject *self, PyObject *args) { + PyObject* socket_capsule; + size_t conn_queue_len = 10; + if (!PyArg_ParseTuple(args, "O|n", &socket_capsule, &conn_queue_len)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(socket_capsule, "csp_socket_t")) { + return NULL; // TypeError is thrown + } + + void* sock = PyCapsule_GetPointer(socket_capsule, "csp_socket_t"); + return Py_BuildValue("i", csp_listen((csp_socket_t*)sock, conn_queue_len)); +} + +/* int csp_bind(csp_socket_t *socket, uint8_t port); */ +static PyObject* pycsp_bind(PyObject *self, PyObject *args) { + PyObject* socket_capsule; + uint8_t port; + if (!PyArg_ParseTuple(args, "Ob", &socket_capsule, &port)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(socket_capsule, "csp_socket_t")) { + return NULL; // TypeError is thrown + } + + void* sock = PyCapsule_GetPointer(socket_capsule, "csp_socket_t"); + return Py_BuildValue("i", csp_bind((csp_socket_t*)sock, port)); +} + +/* int csp_route_start_task(unsigned int task_stack_size, unsigned int priority); */ +static PyObject* pycsp_route_start_task(PyObject *self, PyObject *args) { + unsigned int priority = CSP_PRIO_NORM; + if (!PyArg_ParseTuple(args, "|I", &priority)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_route_start_task(0, priority)); +} + +/* + * int csp_ping(uint8_t node, uint32_t timeout, + * unsigned int size, uint8_t conn_options); + */ +static PyObject* pycsp_ping(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout = 500; + unsigned int size = 100; + uint8_t conn_options = CSP_O_NONE; + if (!PyArg_ParseTuple(args, "b|IIb", &node, &timeout, &size, &conn_options)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_ping(node, timeout, size, conn_options)); +} + +/* + * void csp_reboot(uint8_t node); + */ +static PyObject* pycsp_reboot(PyObject *self, PyObject *args) { + uint8_t node; + if (!PyArg_ParseTuple(args, "b", &node)) { + return NULL; // TypeError is thrown + } + + csp_reboot(node); + Py_RETURN_NONE; +} + +/* + * void csp_shutdown(uint8_t node); + */ +static PyObject* pycsp_shutdown(PyObject *self, PyObject *args) { + uint8_t node; + if (!PyArg_ParseTuple(args, "b", &node)) { + return NULL; // TypeError is thrown + } + + csp_shutdown(node); + Py_RETURN_NONE; +} + +/* + * void csp_rdp_set_opt(unsigned int window_size, + * unsigned int conn_timeout_ms, + * unsigned int packet_timeout_ms, + * unsigned int delayed_acks, + * unsigned int ack_timeout, + * unsigned int ack_delay_count); + */ +static PyObject* pycsp_rdp_set_opt(PyObject *self, PyObject *args) { + unsigned int window_size; + unsigned int conn_timeout_ms; + unsigned int packet_timeout_ms; + unsigned int delayed_acks; + unsigned int ack_timeout; + unsigned int ack_delay_count; + if (!PyArg_ParseTuple(args, "IIIIII", &window_size, &conn_timeout_ms, + &packet_timeout_ms, &delayed_acks, + &ack_timeout, &ack_delay_count)) { + return NULL; // TypeError is thrown + } +#ifdef CSP_USE_RDP + csp_rdp_set_opt(window_size, conn_timeout_ms, packet_timeout_ms, + delayed_acks, ack_timeout, ack_delay_count); +#endif + Py_RETURN_NONE; +} + +/* + * void csp_rdp_get_opt(unsigned int *window_size, + * unsigned int *conn_timeout_ms, + * unsigned int *packet_timeout_ms, + * unsigned int *delayed_acks, + * unsigned int *ack_timeout, + * unsigned int *ack_delay_count); + */ +static PyObject* pycsp_rdp_get_opt(PyObject *self, PyObject *args) { + + unsigned int window_size = 0; + unsigned int conn_timeout_ms = 0; + unsigned int packet_timeout_ms = 0; + unsigned int delayed_acks = 0; + unsigned int ack_timeout = 0; + unsigned int ack_delay_count = 0; +#ifdef CSP_USE_RDP + csp_rdp_get_opt(&window_size, + &conn_timeout_ms, + &packet_timeout_ms, + &delayed_acks, + &ack_timeout, + &ack_delay_count); +#endif + return Py_BuildValue("IIIIII", + window_size, + conn_timeout_ms, + packet_timeout_ms, + delayed_acks, + ack_timeout, + ack_delay_count); +} + + +/* + * + * int csp_xtea_set_key(char *key, uint32_t keylen); + */ +static PyObject* pycsp_xtea_set_key(PyObject *self, PyObject *args) { + char* key; + uint32_t keylen; + if (!PyArg_ParseTuple(args, "si", &key, &keylen)) { + return NULL; // TypeError is thrown + } + return Py_BuildValue("i", csp_xtea_set_key(key, keylen)); +} +/** + * csp/csp_rtable.h + */ + +/* + * int csp_rtable_set(uint8_t node, uint8_t mask, + * csp_iface_t *ifc, uint8_t mac); + */ +static PyObject* pycsp_rtable_set(PyObject *self, PyObject *args) { + uint8_t node; + uint8_t mask; + char* interface_name; + uint8_t mac = CSP_NODE_MAC; + if (!PyArg_ParseTuple(args, "bbs|b", &node, &mask, &interface_name, &mac)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_rtable_set(node, + mask, + csp_iflist_get_by_name(interface_name), + mac)); +} + +/* + * void csp_rtable_clear(void); + */ +static PyObject* pycsp_rtable_clear(PyObject *self, PyObject *args) { + csp_rtable_clear(); + Py_RETURN_NONE; +} + +/* +* int csp_rtable_check(const char * buffer) +*/ +static PyObject* pycsp_rtable_check(PyObject *self, PyObject *args) { + char* buffer; + if (!PyArg_ParseTuple(args, "s", &buffer)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_rtable_check(buffer)); +} + +/* +* void csp_rtable_load(const char * buffer) +*/ +static PyObject* pycsp_rtable_load(PyObject *self, PyObject *args) { + char* buffer; + if (!PyArg_ParseTuple(args, "s", &buffer)) { + return NULL; // TypeError is thrown + } + + csp_rtable_load(buffer); + Py_RETURN_NONE; +} + +/** + * csp/csp_buffer.h + */ + +/* + * int csp_buffer_init(int count, int size); + */ +static PyObject* pycsp_buffer_init(PyObject *self, PyObject *args) { + int count; + int size; + if (!PyArg_ParseTuple(args, "ii", &count, &size)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_buffer_init(count, size)); +} + +/* + * void * csp_buffer_get(size_t size); + */ +static PyObject* pycsp_buffer_get(PyObject *self, PyObject *args) { + size_t size; + if (!PyArg_ParseTuple(args, "n", &size)) { + return NULL; // TypeError is thrown + } + + void* packet = csp_buffer_get(size); + if (packet == NULL) { + Py_RETURN_NONE; + } + + return PyCapsule_New(packet, "csp_packet_t", NULL); +} +/* + * void csp_buffer_free(void *packet); + */ +static PyObject* pycsp_buffer_free(PyObject *self, PyObject *args) { + PyObject* packet_capsule; + if (!PyArg_ParseTuple(args, "O", &packet_capsule)) { + return NULL; // TypeError is thrown + } + + + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_buffer_free(PyCapsule_GetPointer(packet_capsule, "csp_packet_t")); + Py_RETURN_NONE; +} + +/* + * int csp_buffer_remaining(void); + */ +static PyObject* pycsp_buffer_remaining(PyObject *self, PyObject *args) { + return Py_BuildValue("i", csp_buffer_remaining()); +} + +/** + * csp/csp_cmp.h + */ + +/* + * static inline int csp_cmp_ident(uint8_t node, uint32_t timeout, + * struct csp_cmp_message *msg) + */ +static PyObject* pycsp_cmp_ident(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout = 500; + if (!PyArg_ParseTuple(args, "b|i", &node, &timeout)) { + return NULL; // TypeError is thrown + } + + struct csp_cmp_message msg; + int rc = csp_cmp_ident(node, timeout, &msg); + return Py_BuildValue("isssss", + rc, + msg.ident.hostname, + msg.ident.model, + msg.ident.revision, + msg.ident.date, + msg.ident.time); +} + +/* + * static inline int csp_cmp_route_set(uint8_t node, uint32_t timeout, + * struct csp_cmp_message *msg) + */ +static PyObject* pycsp_cmp_route_set(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout = 500; + uint8_t addr; + uint8_t mac; + char* ifstr; + if (!PyArg_ParseTuple(args, "bibbs", &node, &timeout, &addr, &mac, &ifstr)) { + return NULL; // TypeError is thrown + } + + struct csp_cmp_message msg; + msg.route_set.dest_node = addr; + msg.route_set.next_hop_mac = mac; + strncpy(msg.route_set.interface, ifstr, CSP_CMP_ROUTE_IFACE_LEN); + int rc = csp_cmp_route_set(node, timeout, &msg); + return Py_BuildValue("i", + rc); +} + +/* static inline int pycsp_cmp_peek(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg); */ +static PyObject* pycsp_cmp_peek(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout; + uint8_t len; + uint32_t addr; + Py_buffer outbuf; + + if (!PyArg_ParseTuple(args, "biibw*", &node, &timeout, &addr, &len, &outbuf)) { + Py_RETURN_NONE; + } + + if (len > CSP_CMP_PEEK_MAX_LEN) { + len = CSP_CMP_PEEK_MAX_LEN; + } + struct csp_cmp_message msg; + msg.peek.addr = csp_hton32(addr); + msg.peek.len = len; + int rc = csp_cmp_peek(node, timeout, &msg); + if (rc != CSP_ERR_NONE) { + Py_RETURN_NONE; + } + memcpy(outbuf.buf, msg.peek.data, len); + outbuf.len = len; + + return Py_BuildValue("i", rc); +} + +/* static inline int pycsp_cmp_poke(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg); */ +static PyObject* pycsp_cmp_poke(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout; + uint8_t len; + uint32_t addr; + Py_buffer inbuf; + + if (!PyArg_ParseTuple(args, "biibw*", &node, &timeout, &addr, &len, &inbuf)) { + Py_RETURN_NONE; + } + + if (len > CSP_CMP_POKE_MAX_LEN) { + len = CSP_CMP_POKE_MAX_LEN; + } + struct csp_cmp_message msg; + msg.poke.addr = csp_hton32(addr); + msg.poke.len = len; + memcpy(msg.poke.data, inbuf.buf, len); + int rc = csp_cmp_poke(node, timeout, &msg); + if (rc != CSP_ERR_NONE) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", rc); +} + +/* static inline int csp_cmp_clock(uint8_t node, uint32_t timeout, struct csp_cmp_message *msg); */ +static PyObject* pycsp_cmp_clock(PyObject *self, PyObject *args) { + uint8_t node; + uint32_t timeout; + uint32_t sec; + uint32_t nsec; + if (!PyArg_ParseTuple(args, "bIII", &node, &timeout, &sec, &nsec)) { + Py_RETURN_NONE; + } + + struct csp_cmp_message msg; + msg.clock.tv_sec = csp_hton32(sec); + msg.clock.tv_nsec = csp_hton32(nsec); + return Py_BuildValue("i", csp_cmp_clock(node, timeout, &msg)); +} + +/** + * csp/interfaces/csp_if_zmqhub.h + */ + +/* + * int csp_zmqhub_init(char addr, char * host); + */ +static PyObject* pycsp_zmqhub_init(PyObject *self, PyObject *args) { + char addr; + char* host; + if (!PyArg_ParseTuple(args, "bs", &addr, &host)) { + return NULL; // TypeError is thrown + } + + return Py_BuildValue("i", csp_zmqhub_init(addr, host)); +} + +/** + * csp/drivers/can_socketcan.h + */ + +/* + * csp_iface_t * csp_can_socketcan_init(const char * ifc, int bitrate, int promisc); + */ +static PyObject* pycsp_can_socketcan_init(PyObject *self, PyObject *args) +{ + char* ifc; + int bitrate = 1000000; + int promisc = 0; + + if (!PyArg_ParseTuple(args, "s|ii", &ifc, &bitrate, &promisc)) + { + return NULL; + } + + csp_can_socketcan_init(ifc, bitrate, promisc); + Py_RETURN_NONE; +} + + +/** + * csp/interfaces/csp_if_kiss.h + */ + +/* + * int csp_kiss_init(char addr, char * host); + */ +static PyObject* pycsp_kiss_init(PyObject *self, PyObject *args) { + char* device; + uint32_t baudrate = 500000; + uint32_t mtu = 512; + const char* if_name = "KISS"; + if (!PyArg_ParseTuple(args, "s|IIs", &device, &baudrate, &mtu, &if_name)) { + return NULL; // TypeError is thrown + } + + static csp_iface_t csp_if_kiss; + static csp_kiss_handle_t csp_kiss_driver; + csp_if_kiss.mtu = (uint16_t) mtu; + struct usart_conf conf = {.device = device, .baudrate = baudrate}; + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, usart_putc, usart_insert, if_name); + usart_init(&conf); + + void my_usart_rx(uint8_t * buf, int len, void * pxTaskWoken) { + csp_kiss_rx(&csp_if_kiss, buf, len, pxTaskWoken); + } + usart_set_callback(my_usart_rx); + + Py_RETURN_NONE; +} + +/** + * Helpers - accessing csp_packet_t members + */ +static PyObject* pycsp_packet_set_data(PyObject *self, PyObject *args) { + PyObject* packet_capsule; + Py_buffer data; + if (!PyArg_ParseTuple(args, "Ow*", &packet_capsule, &data)) { + return NULL; // TypeError is thrown + } + + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_packet_t* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + + memcpy((char *)packet->data, data.buf, data.len); + packet->length = data.len; + + Py_RETURN_NONE; +} +static PyObject* pycsp_packet_get_data(PyObject *self, PyObject *packet_capsule) { + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_packet_t* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); +#ifdef IS_PY3 + return Py_BuildValue("y#", packet->data, packet->length); +#else + return Py_BuildValue("s#", packet->data, packet->length); +#endif +} + +static PyObject* pycsp_packet_get_length(PyObject *self, PyObject *packet_capsule) { + if (!is_capsule_of_type(packet_capsule, "csp_packet_t")) { + return NULL; // TypeError is thrown + } + + csp_packet_t* packet = PyCapsule_GetPointer(packet_capsule, "csp_packet_t"); + return Py_BuildValue("H", packet->length); +} + +static PyMethodDef methods[] = { + + /* csp/csp.h */ + {"service_handler", pycsp_service_handler, METH_VARARGS, ""}, + {"init", pycsp_init, METH_VARARGS, ""}, + {"set_hostname", pycsp_set_hostname, METH_VARARGS, ""}, + {"get_hostname", pycsp_get_hostname, METH_NOARGS, ""}, + {"set_model", pycsp_set_model, METH_VARARGS, ""}, + {"get_model", pycsp_get_model, METH_NOARGS, ""}, + {"set_revision", pycsp_set_revision, METH_VARARGS, ""}, + {"get_revision", pycsp_get_revision, METH_NOARGS, ""}, + {"socket", pycsp_socket, METH_VARARGS, ""}, + {"accept", pycsp_accept, METH_VARARGS, ""}, + {"read", pycsp_read, METH_VARARGS, ""}, + {"send", pycsp_send, METH_VARARGS, ""}, + {"transaction", pycsp_transaction, METH_VARARGS, ""}, + {"sendto_reply", pycsp_sendto_reply, METH_VARARGS, ""}, + {"sendto", pycsp_sendto, METH_VARARGS, ""}, + {"connect", pycsp_connect, METH_VARARGS, ""}, + {"close", pycsp_close, METH_O, ""}, + {"conn_dport", pycsp_conn_dport, METH_O, ""}, + {"conn_sport", pycsp_conn_sport, METH_O, ""}, + {"conn_dst", pycsp_conn_dst, METH_O, ""}, + {"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, ""}, + {"ping", pycsp_ping, METH_VARARGS, ""}, + {"reboot", pycsp_reboot, METH_VARARGS, ""}, + {"shutdown", pycsp_shutdown, METH_VARARGS, ""}, + {"rdp_set_opt", pycsp_rdp_set_opt, METH_VARARGS, ""}, + {"rdp_get_opt", pycsp_rdp_get_opt, METH_NOARGS, ""}, + {"xtea_set_key", pycsp_xtea_set_key, METH_VARARGS, ""}, + + /* csp/csp_rtable.h */ + {"rtable_set", pycsp_rtable_set, METH_VARARGS, ""}, + {"rtable_clear", pycsp_rtable_clear, METH_NOARGS, ""}, + {"rtable_check", pycsp_rtable_check, METH_VARARGS, ""}, + {"rtable_load", pycsp_rtable_load, METH_VARARGS, ""}, + + /* csp/csp_buffer.h */ + {"buffer_init", pycsp_buffer_init, METH_VARARGS, ""}, + {"buffer_free", pycsp_buffer_free, METH_VARARGS, ""}, + {"buffer_get", pycsp_buffer_get, METH_VARARGS, ""}, + {"buffer_remaining", pycsp_buffer_remaining, METH_NOARGS, ""}, + + /* csp/csp_cmp.h */ + {"cmp_ident", pycsp_cmp_ident, METH_VARARGS, ""}, + {"cmp_route_set", pycsp_cmp_route_set, METH_VARARGS, ""}, + {"cmp_peek", pycsp_cmp_peek, METH_VARARGS, ""}, + {"cmp_poke", pycsp_cmp_poke, METH_VARARGS, ""}, + {"cmp_clock", pycsp_cmp_clock, METH_VARARGS, ""}, + + + /* csp/interfaces/csp_if_zmqhub.h */ + {"zmqhub_init", pycsp_zmqhub_init, METH_VARARGS, ""}, + {"kiss_init", pycsp_kiss_init, METH_VARARGS, ""}, + + /* csp/drivers/can_socketcan.h */ + {"can_socketcan_init", pycsp_can_socketcan_init, METH_VARARGS, ""}, + + /* helpers */ + {"packet_get_length", pycsp_packet_get_length, METH_O, ""}, + {"packet_get_data", pycsp_packet_get_data, METH_O, ""}, + {"packet_set_data", pycsp_packet_set_data, METH_VARARGS, ""}, + + /* sentinel */ + {NULL, NULL, 0, NULL} +}; + +#ifdef IS_PY3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libcsp_py3", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifdef IS_PY3 +PyMODINIT_FUNC PyInit_libcsp_py3(void) { +#else + PyMODINIT_FUNC initlibcsp_py2(void) { +#endif + + PyObject* m; + +#ifdef IS_PY3 + m = PyModule_Create(&moduledef); +#else + m = Py_InitModule("libcsp_py2", methods); +#endif + /** + * csp/csp_types.h + */ + + /* RESERVED PORTS */ + PyModule_AddIntConstant(m, "CSP_CMP", CSP_CMP); + PyModule_AddIntConstant(m, "CSP_PING", CSP_PING); + PyModule_AddIntConstant(m, "CSP_PS", CSP_PS); + PyModule_AddIntConstant(m, "CSP_MEMFREE", CSP_MEMFREE); + PyModule_AddIntConstant(m, "CSP_REBOOT", CSP_REBOOT); + PyModule_AddIntConstant(m, "CSP_BUF_FREE", CSP_BUF_FREE); + PyModule_AddIntConstant(m, "CSP_UPTIME", CSP_UPTIME); + PyModule_AddIntConstant(m, "CSP_ANY", CSP_MAX_BIND_PORT + 1); + PyModule_AddIntConstant(m, "CSP_PROMISC", CSP_MAX_BIND_PORT + 2); + + /* PRIORITIES */ + PyModule_AddIntConstant(m, "CSP_PRIO_CRITICAL", CSP_PRIO_CRITICAL); + PyModule_AddIntConstant(m, "CSP_PRIO_HIGH", CSP_PRIO_HIGH); + PyModule_AddIntConstant(m, "CSP_PRIO_NORM", CSP_PRIO_NORM); + PyModule_AddIntConstant(m, "CSP_PRIO_LOW", CSP_PRIO_LOW); + + /* FLAGS */ + PyModule_AddIntConstant(m, "CSP_FFRAG", CSP_FFRAG); + PyModule_AddIntConstant(m, "CSP_FHMAC", CSP_FHMAC); + PyModule_AddIntConstant(m, "CSP_FXTEA", CSP_FXTEA); + PyModule_AddIntConstant(m, "CSP_FRDP", CSP_FRDP); + PyModule_AddIntConstant(m, "CSP_FCRC32", CSP_FCRC32); + + /* SOCKET OPTIONS */ + PyModule_AddIntConstant(m, "CSP_SO_NONE", CSP_SO_NONE); + PyModule_AddIntConstant(m, "CSP_SO_RDPREQ", CSP_SO_RDPREQ); + PyModule_AddIntConstant(m, "CSP_SO_RDPPROHIB", CSP_SO_RDPPROHIB); + PyModule_AddIntConstant(m, "CSP_SO_HMACREQ", CSP_SO_HMACREQ); + PyModule_AddIntConstant(m, "CSP_SO_HMACPROHIB", CSP_SO_HMACPROHIB); + PyModule_AddIntConstant(m, "CSP_SO_XTEAREQ", CSP_SO_XTEAREQ); + PyModule_AddIntConstant(m, "CSP_SO_XTEAPROHIB", CSP_SO_XTEAPROHIB); + PyModule_AddIntConstant(m, "CSP_SO_CRC32REQ", CSP_SO_CRC32REQ); + PyModule_AddIntConstant(m, "CSP_SO_CRC32PROHIB", CSP_SO_CRC32PROHIB); + PyModule_AddIntConstant(m, "CSP_SO_CONN_LESS", CSP_SO_CONN_LESS); + + /* CONNECT OPTIONS */ + PyModule_AddIntConstant(m, "CSP_O_NONE", CSP_O_NONE); + PyModule_AddIntConstant(m, "CSP_O_RDP", CSP_O_RDP); + PyModule_AddIntConstant(m, "CSP_O_NORDP", CSP_O_NORDP); + PyModule_AddIntConstant(m, "CSP_O_HMAC", CSP_O_HMAC); + PyModule_AddIntConstant(m, "CSP_O_NOHMAC", CSP_O_NOHMAC); + PyModule_AddIntConstant(m, "CSP_O_XTEA", CSP_O_XTEA); + PyModule_AddIntConstant(m, "CSP_O_NOXTEA", CSP_O_NOXTEA); + PyModule_AddIntConstant(m, "CSP_O_CRC32", CSP_O_CRC32); + PyModule_AddIntConstant(m, "CSP_O_NOCRC32", CSP_O_NOCRC32); + + + /** + * csp/csp_error.h + */ + + PyModule_AddIntConstant(m, "CSP_ERR_NONE", CSP_ERR_NONE); + PyModule_AddIntConstant(m, "CSP_ERR_NOMEM", CSP_ERR_NOMEM); + PyModule_AddIntConstant(m, "CSP_ERR_INVAL", CSP_ERR_INVAL); + PyModule_AddIntConstant(m, "CSP_ERR_TIMEDOUT", CSP_ERR_TIMEDOUT); + PyModule_AddIntConstant(m, "CSP_ERR_USED", CSP_ERR_USED); + PyModule_AddIntConstant(m, "CSP_ERR_NOTSUP", CSP_ERR_NOTSUP); + PyModule_AddIntConstant(m, "CSP_ERR_BUSY", CSP_ERR_BUSY); + PyModule_AddIntConstant(m, "CSP_ERR_ALREADY", CSP_ERR_ALREADY); + PyModule_AddIntConstant(m, "CSP_ERR_RESET", CSP_ERR_RESET); + PyModule_AddIntConstant(m, "CSP_ERR_NOBUFS", CSP_ERR_NOBUFS); + 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_HMAC", CSP_ERR_HMAC); + PyModule_AddIntConstant(m, "CSP_ERR_XTEA", CSP_ERR_XTEA); + PyModule_AddIntConstant(m, "CSP_ERR_CRC32", CSP_ERR_CRC32); + + /** + * csp/rtable.h + */ + PyModule_AddIntConstant(m, "CSP_NODE_MAC", CSP_NODE_MAC); + +#ifdef IS_PY3 + return m; +#endif + } + diff --git a/gomspace/libgscsp/lib/libcsp/src/crypto/csp_hmac.c b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_hmac.c new file mode 100644 index 00000000..ae7fbb00 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_hmac.c @@ -0,0 +1,202 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Hash-based Message Authentication Code - based on code from libtom.org */ + +#include +#include +#include + +/* CSP includes */ +#include + +#include +#include + +#ifdef CSP_USE_HMAC + +#define HMAC_KEY_LENGTH 16 + +/* HMAC key */ +static uint8_t csp_hmac_key[HMAC_KEY_LENGTH]; + +/* HMAC state structure */ +typedef struct { + csp_sha1_state md; + uint8_t key[SHA1_BLOCKSIZE]; +} hmac_state; + +static int csp_hmac_init(hmac_state * hmac, const uint8_t * key, uint32_t keylen) { + uint32_t i; + uint8_t buf[SHA1_BLOCKSIZE]; + + /* NULL pointer and key check */ + if (!hmac || !key || keylen < 1) + return CSP_ERR_INVAL; + + /* Make sure we have a large enough key */ + if(keylen > SHA1_BLOCKSIZE) { + csp_sha1_memory(key, keylen, hmac->key); + if(SHA1_DIGESTSIZE < SHA1_BLOCKSIZE) + memset((hmac->key) + SHA1_DIGESTSIZE, 0, (size_t)(SHA1_BLOCKSIZE - SHA1_DIGESTSIZE)); + } else { + memcpy(hmac->key, key, (size_t)keylen); + if(keylen < SHA1_BLOCKSIZE) + memset((hmac->key) + keylen, 0, (size_t)(SHA1_BLOCKSIZE - keylen)); + } + + /* Create the initial vector */ + for(i = 0; i < SHA1_BLOCKSIZE; i++) + buf[i] = hmac->key[i] ^ 0x36; + + /* Prepend to the hash data */ + csp_sha1_init(&hmac->md); + csp_sha1_process(&hmac->md, buf, SHA1_BLOCKSIZE); + + return CSP_ERR_NONE; +} + +static int csp_hmac_process(hmac_state * hmac, const uint8_t * in, uint32_t inlen) { + + /* NULL pointer check */ + if (!hmac || !in) + return CSP_ERR_INVAL; + + /* Process data */ + csp_sha1_process(&hmac->md, in, inlen); + + return CSP_ERR_NONE; +} + +static int csp_hmac_done(hmac_state * hmac, uint8_t * out) { + + uint32_t i; + uint8_t buf[SHA1_BLOCKSIZE]; + uint8_t isha[SHA1_DIGESTSIZE]; + + if (!hmac || !out) + return CSP_ERR_INVAL; + + /* Get the hash of the first HMAC vector plus the data */ + csp_sha1_done(&hmac->md, isha); + + /* Create the second HMAC vector vector */ + for(i = 0; i < SHA1_BLOCKSIZE; i++) + buf[i] = hmac->key[i] ^ 0x5C; + + /* Now calculate the outer hash */ + csp_sha1_init(&hmac->md); + csp_sha1_process(&hmac->md, buf, SHA1_BLOCKSIZE); + csp_sha1_process(&hmac->md, isha, SHA1_DIGESTSIZE); + csp_sha1_done(&hmac->md, buf); + + /* Copy to output */ + for (i = 0; i < SHA1_DIGESTSIZE; i++) + out[i] = buf[i]; + + return CSP_ERR_NONE; +} + +int csp_hmac_memory(const uint8_t * key, uint32_t keylen, const uint8_t * data, uint32_t datalen, uint8_t * hmac) { + hmac_state state; + + /* NULL pointer check */ + if (!key || !data || !hmac) + return CSP_ERR_INVAL; + + /* Init HMAC state */ + if (csp_hmac_init(&state, key, keylen) != 0) + return CSP_ERR_INVAL; + + /* Process data */ + if (csp_hmac_process(&state, data, datalen) != 0) + return CSP_ERR_INVAL; + + /* Output HMAC */ + if (csp_hmac_done(&state, hmac) != 0) + return CSP_ERR_INVAL; + + return CSP_ERR_NONE; +} + +int csp_hmac_set_key(char * key, uint32_t keylen) { + + /* Use SHA1 as KDF */ + uint8_t hash[SHA1_DIGESTSIZE]; + csp_sha1_memory((uint8_t *)key, keylen, hash); + + /* Copy key */ + memcpy(csp_hmac_key, hash, HMAC_KEY_LENGTH); + + return CSP_ERR_NONE; + +} + +int csp_hmac_append(csp_packet_t * packet, bool include_header) { + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + uint8_t hmac[SHA1_DIGESTSIZE]; + + /* Calculate HMAC */ + if (include_header) { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, (uint8_t *) &packet->id, packet->length + sizeof(packet->id), hmac); + } else { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, packet->data, packet->length, hmac); + } + + /* Truncate hash and copy to packet */ + memcpy(&packet->data[packet->length], hmac, CSP_HMAC_LENGTH); + packet->length += CSP_HMAC_LENGTH; + + return CSP_ERR_NONE; + +} + +int csp_hmac_verify(csp_packet_t * packet, bool include_header) { + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + uint8_t hmac[SHA1_DIGESTSIZE]; + + /* Calculate HMAC */ + if (include_header) { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, (uint8_t *) &packet->id, packet->length + sizeof(packet->id) - CSP_HMAC_LENGTH, hmac); + } else { + csp_hmac_memory(csp_hmac_key, HMAC_KEY_LENGTH, packet->data, packet->length - CSP_HMAC_LENGTH, hmac); + } + + /* Compare calculated HMAC with packet header */ + if (memcmp(&packet->data[packet->length] - CSP_HMAC_LENGTH, hmac, CSP_HMAC_LENGTH) != 0) { + /* HMAC failed */ + return CSP_ERR_HMAC; + } else { + /* Strip HMAC */ + packet->length -= CSP_HMAC_LENGTH; + return CSP_ERR_NONE; + } + +} + +#endif // CSP_USE_HMAC diff --git a/gomspace/libgscsp/lib/libcsp/src/crypto/csp_sha1.c b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_sha1.c new file mode 100644 index 00000000..6c3920e9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_sha1.c @@ -0,0 +1,217 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Code originally from Python's SHA1 Module, who based it on libtom.org */ + +#include +#include + +/* CSP includes */ +#include + +#include + +#if defined(CSP_USE_HMAC) || defined(CSP_USE_XTEA) + +/* Rotate left macro */ +#define ROL(x,y) (((x) << (y)) | ((x) >> (32-y))) + +/* Endian Neutral macros that work on all platforms */ +#define STORE32H(x, y) do { (y)[0] = (uint8_t)(((x) >> 24) & 0xff); \ + (y)[1] = (uint8_t)(((x) >> 16) & 0xff); \ + (y)[2] = (uint8_t)(((x) >> 8) & 0xff); \ + (y)[3] = (uint8_t)(((x) >> 0) & 0xff); } while (0) + +#define LOAD32H(x, y) do { (x) = ((uint32_t)((y)[0] & 0xff) << 24) | \ + ((uint32_t)((y)[1] & 0xff) << 16) | \ + ((uint32_t)((y)[2] & 0xff) << 8) | \ + ((uint32_t)((y)[3] & 0xff) << 0); } while (0) + +#define STORE64H(x, y) do { (y)[0] = (uint8_t)(((x) >> 56) & 0xff); \ + (y)[1] = (uint8_t)(((x) >> 48) & 0xff); \ + (y)[2] = (uint8_t)(((x) >> 40) & 0xff); \ + (y)[3] = (uint8_t)(((x) >> 32) & 0xff); \ + (y)[4] = (uint8_t)(((x) >> 24) & 0xff); \ + (y)[5] = (uint8_t)(((x) >> 16) & 0xff); \ + (y)[6] = (uint8_t)(((x) >> 8) & 0xff); \ + (y)[7] = (uint8_t)(((x) >> 0) & 0xff); } while (0) + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +/* SHA1 macros */ +#define F0(x,y,z) (z ^ (x & (y ^ z))) +#define F1(x,y,z) (x ^ y ^ z) +#define F2(x,y,z) ((x & y) | (z & (x | y))) +#define F3(x,y,z) (x ^ y ^ z) + +#define FF_0(a, b, c, d, e, i) do {e = (ROL(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROL(b, 30);} while (0) +#define FF_1(a, b, c, d, e, i) do {e = (ROL(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROL(b, 30);} while (0) +#define FF_2(a, b, c, d, e, i) do {e = (ROL(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROL(b, 30);} while (0) +#define FF_3(a, b, c, d, e, i) do {e = (ROL(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROL(b, 30);} while (0) + +static void csp_sha1_compress(csp_sha1_state * sha1, const uint8_t * buf) { + + uint32_t a, b, c, d, e, W[80], i; + + /* Copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + LOAD32H(W[i], buf + (4*i)); + + /* Copy state */ + a = sha1->state[0]; + b = sha1->state[1]; + c = sha1->state[2]; + d = sha1->state[3]; + e = sha1->state[4]; + + /* Expand it */ + for (i = 16; i < 80; i++) + W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); + + /* Compress */ + i = 0; + + /* Round one */ + for (; i < 20;) { + FF_0(a, b, c, d, e, i++); + FF_0(e, a, b, c, d, i++); + FF_0(d, e, a, b, c, i++); + FF_0(c, d, e, a, b, i++); + FF_0(b, c, d, e, a, i++); + } + + /* Round two */ + for (; i < 40;) { + FF_1(a, b, c, d, e, i++); + FF_1(e, a, b, c, d, i++); + FF_1(d, e, a, b, c, i++); + FF_1(c, d, e, a, b, i++); + FF_1(b, c, d, e, a, i++); + } + + /* Round three */ + for (; i < 60;) { + FF_2(a, b, c, d, e, i++); + FF_2(e, a, b, c, d, i++); + FF_2(d, e, a, b, c, i++); + FF_2(c, d, e, a, b, i++); + FF_2(b, c, d, e, a, i++); + } + + /* Round four */ + for (; i < 80;) { + FF_3(a, b, c, d, e, i++); + FF_3(e, a, b, c, d, i++); + FF_3(d, e, a, b, c, i++); + FF_3(c, d, e, a, b, i++); + FF_3(b, c, d, e, a, i++); + } + + /* Store */ + sha1->state[0] += a; + sha1->state[1] += b; + sha1->state[2] += c; + sha1->state[3] += d; + sha1->state[4] += e; + +} + +void csp_sha1_init(csp_sha1_state * sha1) { + + sha1->state[0] = 0x67452301UL; + sha1->state[1] = 0xefcdab89UL; + sha1->state[2] = 0x98badcfeUL; + sha1->state[3] = 0x10325476UL; + sha1->state[4] = 0xc3d2e1f0UL; + sha1->curlen = 0; + sha1->length = 0; + +} + +void csp_sha1_process(csp_sha1_state * sha1, const uint8_t * in, uint32_t inlen) { + + uint32_t n; + while (inlen > 0) { + if (sha1->curlen == 0 && inlen >= SHA1_BLOCKSIZE) { + csp_sha1_compress(sha1, in); + sha1->length += SHA1_BLOCKSIZE * 8; + in += SHA1_BLOCKSIZE; + inlen -= SHA1_BLOCKSIZE; + } else { + n = MIN(inlen, (SHA1_BLOCKSIZE - sha1->curlen)); + memcpy(sha1->buf + sha1->curlen, in, (size_t)n); + sha1->curlen += n; + in += n; + inlen -= n; + if (sha1->curlen == SHA1_BLOCKSIZE) { + csp_sha1_compress(sha1, sha1->buf); + sha1->length += 8*SHA1_BLOCKSIZE; + sha1->curlen = 0; + } + } + } + +} + +void csp_sha1_done(csp_sha1_state * sha1, uint8_t * out) { + + uint32_t i; + + /* Increase the length of the message */ + sha1->length += sha1->curlen * 8; + + /* Append the '1' bit */ + sha1->buf[sha1->curlen++] = 0x80; + + /* If the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (sha1->curlen > 56) { + while (sha1->curlen < 64) + sha1->buf[sha1->curlen++] = 0; + csp_sha1_compress(sha1, sha1->buf); + sha1->curlen = 0; + } + + /* Pad up to 56 bytes of zeroes */ + while (sha1->curlen < 56) + sha1->buf[sha1->curlen++] = 0; + + /* Store length */ + STORE64H(sha1->length, sha1->buf + 56); + csp_sha1_compress(sha1, sha1->buf); + + /* Copy output */ + for (i = 0; i < 5; i++) + STORE32H(sha1->state[i], out + (4 * i)); + +} + +void csp_sha1_memory(const uint8_t * msg, uint32_t len, uint8_t * hash) { + + csp_sha1_state md; + csp_sha1_init(&md); + csp_sha1_process(&md, msg, len); + csp_sha1_done(&md, hash); + +} + +#endif // CSP_USE_HMAC diff --git a/gomspace/libgscsp/lib/libcsp/src/crypto/csp_xtea.c b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_xtea.c new file mode 100644 index 00000000..718824d1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/crypto/csp_xtea.c @@ -0,0 +1,134 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* Simple implementation of XTEA in CTR mode */ + +#include +#include + +/* CSP includes */ +#include +#include +#include +#include + +#ifdef CSP_USE_XTEA + +#define XTEA_BLOCKSIZE 8 +#define XTEA_ROUNDS 32 +#define XTEA_KEY_LENGTH 16 + +/* XTEA key */ +static uint32_t csp_xtea_key[XTEA_KEY_LENGTH/sizeof(uint32_t)] __attribute__ ((aligned(sizeof(uint32_t)))); + +#define STORE32L(x, y) do { (y)[3] = (uint8_t)(((x) >> 24) & 0xff); \ + (y)[2] = (uint8_t)(((x) >> 16) & 0xff); \ + (y)[1] = (uint8_t)(((x) >> 8) & 0xff); \ + (y)[0] = (uint8_t)(((x) >> 0) & 0xff); } while (0) + +#define LOAD32L(x, y) do { (x) = ((uint32_t)((y)[3] & 0xff) << 24) | \ + ((uint32_t)((y)[2] & 0xff) << 16) | \ + ((uint32_t)((y)[1] & 0xff) << 8) | \ + ((uint32_t)((y)[0] & 0xff) << 0); } while (0) + +/* This function takes 64 bits of data in block and the 128 bits key in key */ +static inline void csp_xtea_encrypt_block(uint8_t *block, uint8_t const *key) { + + uint32_t i, v0, v1, delta = 0x9E3779B9, sum = 0, k[4]; + + LOAD32L(k[0], &key[0]); + LOAD32L(k[1], &key[4]); + LOAD32L(k[2], &key[8]); + LOAD32L(k[3], &key[12]); + + LOAD32L(v0, &block[0]); + LOAD32L(v1, &block[4]); + + for (i = 0; i < XTEA_ROUNDS; i++) { + v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); + sum += delta; + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum >> 11) & 3]); + } + + STORE32L(v0, &block[0]); + STORE32L(v1, &block[4]); + +} + +static inline void csp_xtea_xor_byte(uint8_t * dst, uint8_t * src, uint32_t len) { + + unsigned int i; + for (i = 0; i < len; i++) + dst[i] ^= src[i]; + +} + +int csp_xtea_set_key(char * key, uint32_t keylen) { + + /* Use SHA1 as KDF */ + uint8_t hash[SHA1_DIGESTSIZE]; + csp_sha1_memory((uint8_t *)key, keylen, hash); + + /* Copy key */ + memcpy(csp_xtea_key, hash, XTEA_KEY_LENGTH); + + return CSP_ERR_NONE; + +} + +int csp_xtea_encrypt(uint8_t * plain, const uint32_t len, uint32_t iv[2]) { + + unsigned int i; + uint32_t stream[2]; + + uint32_t blocks = (len + XTEA_BLOCKSIZE - 1)/ XTEA_BLOCKSIZE; + uint32_t remain; + + /* Initialize stream */ + stream[0] = csp_htobe32(iv[0]); + stream[1] = csp_htobe32(iv[1]); + + for (i = 0; i < blocks; i++) { + /* Create stream */ + csp_xtea_encrypt_block((uint8_t *)stream, (uint8_t *)csp_xtea_key); + + /* Calculate remaining bytes */ + remain = len - i * XTEA_BLOCKSIZE; + + /* XOR plain text with stream to generate cipher text */ + csp_xtea_xor_byte(&plain[len - remain], (uint8_t *)stream, remain < XTEA_BLOCKSIZE ? remain : XTEA_BLOCKSIZE); + + /* Increment counter */ + stream[0] = csp_htobe32(iv[0]); + stream[1] = csp_htobe32(iv[1]++); + } + + return CSP_ERR_NONE; + +} + +int csp_xtea_decrypt(uint8_t * cipher, const uint32_t len, uint32_t iv[2]) { + + /* Since we use counter mode, we can reuse the encryption function */ + return csp_xtea_encrypt(cipher, len, iv); + +} + +#endif // CSP_USE_XTEA diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_bridge.c b/gomspace/libgscsp/lib/libcsp/src/csp_bridge.c new file mode 100644 index 00000000..1c579a9f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_bridge.c @@ -0,0 +1,94 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "csp_route.h" +#include "csp_qfifo.h" +#include "csp_io.h" +#include "csp_promisc.h" + +static csp_iface_t* if_a = NULL; +static csp_iface_t* if_b = NULL; + +static CSP_DEFINE_TASK(csp_bridge) { + + csp_qfifo_t input; + csp_packet_t * packet; + + /* Here there be bridging */ + while (1) { + + /* Get next packet to route */ + if (csp_qfifo_read(&input) != CSP_ERR_NONE) + continue; + + packet = input.packet; + + csp_log_packet("Input: Src %u, Dst %u, Dport %u, Sport %u, Pri %u, Flags 0x%02X, Size %"PRIu16, + packet->id.src, packet->id.dst, packet->id.dport, + packet->id.sport, packet->id.pri, packet->id.flags, packet->length); + + /* Here there be promiscuous mode */ +#ifdef CSP_USE_PROMISC + csp_promisc_add(packet); +#endif + + /* Find the opposing interface */ + csp_iface_t * ifout; + if (input.interface == if_a) { + ifout = if_b; + } else { + ifout = if_a; + } + + /* Send to the interface directly, no hassle */ + if (csp_send_direct(packet->id, packet, ifout, 0) != CSP_ERR_NONE) { + csp_log_warn("Router failed to send"); + csp_buffer_free(packet); + } + + /* Next message, please */ + continue; + + } + + return CSP_TASK_RETURN; + +} + +int csp_bridge_start(unsigned int task_stack_size, unsigned int task_priority, csp_iface_t * _if_a, csp_iface_t * _if_b) { + + /* Set static references to A/B side of bridge */ + if_a = _if_a; + if_b = _if_b; + + static csp_thread_handle_t handle; + int ret = csp_thread_create(csp_bridge, "BRIDGE", task_stack_size, NULL, task_priority, &handle); + + if (ret != 0) { + csp_log_error("Failed to start task"); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_buffer.c b/gomspace/libgscsp/lib/libcsp/src/csp_buffer.c new file mode 100644 index 00000000..8947f337 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_buffer.c @@ -0,0 +1,224 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include +#include +#include +#include + +#ifndef CSP_BUFFER_ALIGN +#define CSP_BUFFER_ALIGN (sizeof(int *)) +#endif + +typedef struct csp_skbf_s { + unsigned int refcount; + void * skbf_addr; + char skbf_data[]; +} csp_skbf_t; + +static csp_queue_handle_t csp_buffers; +static char * csp_buffer_pool; +static unsigned int count, size; + +CSP_DEFINE_CRITICAL(csp_critical_lock); + +int csp_buffer_init(int buf_count, int buf_size) { + + unsigned int i; + csp_skbf_t * buf; + + count = buf_count; + size = buf_size + CSP_BUFFER_PACKET_OVERHEAD; + unsigned int skbfsize = (sizeof(csp_skbf_t) + size); + skbfsize = CSP_BUFFER_ALIGN * ((skbfsize + CSP_BUFFER_ALIGN - 1) / CSP_BUFFER_ALIGN); + unsigned int poolsize = count * skbfsize; + + csp_buffer_pool = csp_malloc(poolsize); + if (csp_buffer_pool == NULL) + goto fail_malloc; + + csp_buffers = csp_queue_create(count, sizeof(void *)); + if (!csp_buffers) + goto fail_queue; + + if (CSP_INIT_CRITICAL(csp_critical_lock) != CSP_ERR_NONE) + goto fail_critical; + + memset(csp_buffer_pool, 0, poolsize); + + for (i = 0; i < count; i++) { + + /* We have already taken care of pointer alignment since + * skbfsize is an integer multiple of sizeof(int *) + * but the explicit cast to a void * is still necessary + * to tell the compiler so. + */ + buf = (void *) &csp_buffer_pool[i * skbfsize]; + buf->refcount = 0; + buf->skbf_addr = buf; + + csp_queue_enqueue(csp_buffers, &buf, 0); + + } + + return CSP_ERR_NONE; + +fail_critical: + csp_queue_remove(csp_buffers); +fail_queue: + csp_free(csp_buffer_pool); +fail_malloc: + return CSP_ERR_NOMEM; + +} + +void *csp_buffer_get_isr(size_t buf_size) { + + csp_skbf_t * buffer = NULL; + CSP_BASE_TYPE task_woken = 0; + + if (buf_size + CSP_BUFFER_PACKET_OVERHEAD > size) + return NULL; + + csp_queue_dequeue_isr(csp_buffers, &buffer, &task_woken); + if (buffer == NULL) + return NULL; + + if (buffer != buffer->skbf_addr) + return NULL; + + buffer->refcount++; + return buffer->skbf_data; + +} + +void *csp_buffer_get(size_t buf_size) { + + csp_skbf_t * buffer = NULL; + + if (buf_size + CSP_BUFFER_PACKET_OVERHEAD > size) { + csp_log_error("Attempt to allocate too large block %u", buf_size); + return NULL; + } + + csp_queue_dequeue(csp_buffers, &buffer, 0); + if (buffer == NULL) { + csp_log_error("Out of buffers"); + return NULL; + } + + csp_log_buffer("GET: %p %p", buffer, buffer->skbf_addr); + + if (buffer != buffer->skbf_addr) { + csp_log_error("Corrupt CSP buffer"); + return NULL; + } + + buffer->refcount++; + return buffer->skbf_data; +} + +void csp_buffer_free_isr(void *packet) { + CSP_BASE_TYPE task_woken = 0; + if (!packet) + return; + + csp_skbf_t * buf = packet - sizeof(csp_skbf_t); + + if (((uintptr_t) buf % CSP_BUFFER_ALIGN) > 0) + return; + + if (buf->skbf_addr != buf) + return; + + if (buf->refcount == 0) { + return; + } else if (buf->refcount > 1) { + buf->refcount--; + return; + } else { + buf->refcount = 0; + csp_queue_enqueue_isr(csp_buffers, &buf, &task_woken); + } + +} + +void csp_buffer_free(void *packet) { + if (!packet) { + csp_log_error("Attempt to free null pointer"); + return; + } + + csp_skbf_t * buf = packet - sizeof(csp_skbf_t); + + if (((uintptr_t) buf % CSP_BUFFER_ALIGN) > 0) { + csp_log_error("FREE: Unaligned CSP buffer pointer %p", packet); + return; + } + + if (buf->skbf_addr != buf) { + csp_log_error("FREE: Invalid CSP buffer pointer %p", packet); + return; + } + + if (buf->refcount == 0) { + csp_log_error("FREE: Buffer already free %p", buf); + return; + } else if (buf->refcount > 1) { + buf->refcount--; + csp_log_error("FREE: Buffer %p in use by %u users", buf, buf->refcount); + return; + } else { + buf->refcount = 0; + csp_log_buffer("FREE: %p", buf); + 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) + memcpy(clone, packet, size); + + return clone; + +} + +int csp_buffer_remaining(void) { + return csp_queue_size(csp_buffers); +} + +int csp_buffer_size(void) { + return size; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_conn.c b/gomspace/libgscsp/lib/libcsp/src/csp_conn.c new file mode 100644 index 00000000..7daa569d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_conn.c @@ -0,0 +1,498 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include +#include + +#include +#include +#include +#include +#include + +#include "csp_conn.h" +#include "transport/csp_transport.h" + +/* Static connection pool */ +static csp_conn_t arr_conn[CSP_CONN_MAX]; + +/* Connection pool lock */ +static csp_bin_sem_handle_t conn_lock; + +/* Source port */ +static uint8_t sport; + +/* Source port lock */ +static csp_bin_sem_handle_t sport_lock; + +void csp_conn_check_timeouts(void) { +#ifdef CSP_USE_RDP + int i; + for (i = 0; i < CSP_CONN_MAX; i++) + if (arr_conn[i].state == CONN_OPEN) + if (arr_conn[i].idin.flags & CSP_FRDP) + csp_rdp_check_timeouts(&arr_conn[i]); +#endif +} + +int csp_conn_get_rxq(int prio) { + +#ifdef CSP_USE_QOS + return prio; +#else + return 0; +#endif + +} + +int csp_conn_lock(csp_conn_t * conn, uint32_t timeout) { + + if (csp_mutex_lock(&conn->lock, timeout) != CSP_MUTEX_OK) + return CSP_ERR_TIMEDOUT; + + return CSP_ERR_NONE; + +} + +int csp_conn_unlock(csp_conn_t * conn) { + + csp_mutex_unlock(&conn->lock); + + return CSP_ERR_NONE; + +} + +int csp_conn_enqueue_packet(csp_conn_t * conn, csp_packet_t * packet) { + + if (!conn) + return CSP_ERR_INVAL; + + int rxq; + if (packet != NULL) { + rxq = csp_conn_get_rxq(packet->id.pri); + } else { + rxq = CSP_RX_QUEUES - 1; + } + + if (csp_queue_enqueue(conn->rx_queue[rxq], &packet, 0) != CSP_QUEUE_OK) { + csp_log_error("RX queue %p full with %u items", conn->rx_queue[rxq], csp_queue_size(conn->rx_queue[rxq])); + return CSP_ERR_NOMEM; + } + +#ifdef CSP_USE_QOS + int event = 0; + if (csp_queue_enqueue(conn->rx_event, &event, 0) != CSP_QUEUE_OK) { + csp_log_error("QOS event queue full"); + return CSP_ERR_NOMEM; + } +#endif + + return CSP_ERR_NONE; +} + +int csp_conn_init(void) { + + /* Initialize source port */ + srand(csp_get_ms()); + sport = (rand() % (CSP_ID_PORT_MAX - CSP_MAX_BIND_PORT)) + (CSP_MAX_BIND_PORT + 1); + + if (csp_bin_sem_create(&sport_lock) != CSP_SEMAPHORE_OK) { + csp_log_error("No more memory for sport semaphore"); + return CSP_ERR_NOMEM; + } + + int i, prio; + for (i = 0; i < CSP_CONN_MAX; i++) { + for (prio = 0; prio < CSP_RX_QUEUES; prio++) + arr_conn[i].rx_queue[prio] = csp_queue_create(CSP_RX_QUEUE_LENGTH, sizeof(csp_packet_t *)); + +#ifdef CSP_USE_QOS + arr_conn[i].rx_event = csp_queue_create(CSP_CONN_QUEUE_LENGTH, sizeof(int)); +#endif + arr_conn[i].state = CONN_CLOSED; + + if (csp_mutex_create(&arr_conn[i].lock) != CSP_MUTEX_OK) { + csp_log_error("Failed to create connection lock"); + return CSP_ERR_NOMEM; + } + +#ifdef CSP_USE_RDP + if (csp_rdp_allocate(&arr_conn[i]) != CSP_ERR_NONE) { + csp_log_error("Failed to create queues for RDP in csp_conn_init"); + return CSP_ERR_NOMEM; + } +#endif + } + + if (csp_bin_sem_create(&conn_lock) != CSP_SEMAPHORE_OK) { + csp_log_error("No more memory for conn semaphore"); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} + +csp_conn_t * csp_conn_find(uint32_t id, uint32_t mask) { + + /* Search for matching connection */ + int i; + csp_conn_t * conn; + id = (id & mask); + for (i = 0; i < CSP_CONN_MAX; i++) { + conn = &arr_conn[i]; + if ((conn->state != CONN_CLOSED) && (conn->type == CONN_CLIENT) && ((conn->idin.ext & mask) == id)) + return conn; + } + + return NULL; + +} + +static int csp_conn_flush_rx_queue(csp_conn_t * conn) { + + csp_packet_t * packet; + + int prio; + + /* Flush packet queues */ + for (prio = 0; prio < CSP_RX_QUEUES; prio++) { + while (csp_queue_dequeue(conn->rx_queue[prio], &packet, 0) == CSP_QUEUE_OK) + if (packet != NULL) + csp_buffer_free(packet); + } + + /* Flush event queue */ +#ifdef CSP_USE_QOS + int event; + while (csp_queue_dequeue(conn->rx_event, &event, 0) == CSP_QUEUE_OK); +#endif + + return CSP_ERR_NONE; + +} + +csp_conn_t * csp_conn_allocate(csp_conn_type_t type) { + + int i, j; + static uint8_t csp_conn_last_given = 0; + csp_conn_t * conn; + + if (csp_bin_sem_wait(&conn_lock, 100) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to lock conn array"); + return NULL; + } + + /* Search for free connection */ + i = csp_conn_last_given; + i = (i + 1) % CSP_CONN_MAX; + + for (j = 0; j < CSP_CONN_MAX; j++) { + conn = &arr_conn[i]; + if (conn->state == CONN_CLOSED) + break; + i = (i + 1) % CSP_CONN_MAX; + } + + if (conn->state == CONN_OPEN) { + csp_log_error("No more free connections"); + csp_bin_sem_post(&conn_lock); + return NULL; + } + + conn->idin.ext = 0; + conn->idout.ext = 0; + conn->socket = NULL; + conn->timestamp = 0; + conn->type = type; + conn->state = CONN_OPEN; + + csp_conn_last_given = i; + csp_bin_sem_post(&conn_lock); + + return conn; + +} + +csp_conn_t * csp_conn_new(csp_id_t idin, csp_id_t idout) { + + /* Allocate connection structure */ + csp_conn_t * conn = csp_conn_allocate(CONN_CLIENT); + + if (conn) { + /* No lock is needed here, because nobody else * + * has a reference to this connection yet. */ + conn->idin.ext = idin.ext; + conn->idout.ext = idout.ext; + conn->timestamp = csp_get_ms(); + + /* Ensure connection queue is empty */ + csp_conn_flush_rx_queue(conn); + } + + return conn; + +} + +int csp_close(csp_conn_t * conn) { + + if (conn == NULL) { + csp_log_error("NULL Pointer given to csp_close"); + return CSP_ERR_INVAL; + } + + if (conn->state == CONN_CLOSED) { + csp_log_protocol("Conn already closed"); + return CSP_ERR_NONE; + } + +#ifdef CSP_USE_RDP + /* Ensure RDP knows this connection is closing */ + if (conn->idin.flags & CSP_FRDP || conn->idout.flags & CSP_FRDP) + if (csp_rdp_close(conn) == CSP_ERR_AGAIN) + return CSP_ERR_NONE; +#endif + + /* Lock connection array while closing connection */ + if (csp_bin_sem_wait(&conn_lock, 100) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to lock conn array"); + return CSP_ERR_TIMEDOUT; + } + + /* Set to closed */ + conn->state = CONN_CLOSED; + + /* Ensure connection queue is empty */ + csp_conn_flush_rx_queue(conn); + + if (conn->socket && (conn->type == CONN_SERVER) && (conn->opts & (CSP_SO_CONN_LESS | CSP_SO_INTERNAL_LISTEN))) { + csp_queue_remove(conn->socket); + conn->socket = NULL; + } + + /* Reset RDP state */ +#ifdef CSP_USE_RDP + if (conn->idin.flags & CSP_FRDP) + csp_rdp_flush_all(conn); +#endif + + /* Unlock connection array */ + csp_bin_sem_post(&conn_lock); + + return CSP_ERR_NONE; +} + +csp_conn_t * csp_connect(uint8_t prio, uint8_t dest, uint8_t dport, uint32_t timeout, uint32_t opts) { + + /* Force options on all connections */ + opts |= CSP_CONNECTION_SO; + + /* Generate identifier */ + csp_id_t incoming_id, outgoing_id; + incoming_id.pri = prio; + incoming_id.dst = csp_get_address(); + incoming_id.src = dest; + incoming_id.sport = dport; + incoming_id.flags = 0; + outgoing_id.pri = prio; + outgoing_id.dst = dest; + outgoing_id.src = csp_get_address(); + outgoing_id.dport = dport; + outgoing_id.flags = 0; + + /* Set connection options */ + if (opts & CSP_O_NOCRC32) { + opts &= ~CSP_O_CRC32; + } + + if (opts & CSP_O_RDP) { +#ifdef CSP_USE_RDP + incoming_id.flags |= CSP_FRDP; + outgoing_id.flags |= CSP_FRDP; +#else + csp_log_error("Attempt to create RDP connection, but CSP was compiled without RDP support"); + return NULL; +#endif + } + + if (opts & CSP_O_HMAC) { +#ifdef CSP_USE_HMAC + outgoing_id.flags |= CSP_FHMAC; + incoming_id.flags |= CSP_FHMAC; +#else + csp_log_error("Attempt to create HMAC authenticated connection, but CSP was compiled without HMAC support"); + return NULL; +#endif + } + + if (opts & CSP_O_XTEA) { +#ifdef CSP_USE_XTEA + outgoing_id.flags |= CSP_FXTEA; + incoming_id.flags |= CSP_FXTEA; +#else + csp_log_error("Attempt to create XTEA encrypted connection, but CSP was compiled without XTEA support"); + return NULL; +#endif + } + + if (opts & CSP_O_CRC32) { +#ifdef CSP_USE_CRC32 + outgoing_id.flags |= CSP_FCRC32; + incoming_id.flags |= CSP_FCRC32; +#else + csp_log_error("Attempt to create CRC32 validated connection, but CSP was compiled without CRC32 support"); + return NULL; +#endif + } + + /* Find an unused ephemeral port */ + csp_conn_t * conn = NULL; + + /* Wait for sport lock - note that csp_conn_new(..) is called inside the lock! */ + if (csp_bin_sem_wait(&sport_lock, 1000) != CSP_SEMAPHORE_OK) + return NULL; + + const uint8_t start = sport; + while (++sport != start) { + if (sport > CSP_ID_PORT_MAX) + sport = CSP_MAX_BIND_PORT + 1; + + outgoing_id.sport = sport; + incoming_id.dport = sport; + + /* Match on destination port of _incoming_ identifier */ + if (csp_conn_find(incoming_id.ext, CSP_ID_DPORT_MASK) == NULL) { + /* Break - we found an unused ephemeral port + allocate connection while locked to mark port in use */ + conn = csp_conn_new(incoming_id, outgoing_id); + break; + } + } + + /* Post sport lock */ + csp_bin_sem_post(&sport_lock); + + if (conn == NULL) + return NULL; + + /* Set connection options */ + conn->opts = opts; + +#ifdef CSP_USE_RDP + /* Call Transport Layer connect */ + if (outgoing_id.flags & CSP_FRDP) { + /* If the transport layer has failed to connect + * deallocate connection structure again and return NULL */ + if (csp_rdp_connect(conn, timeout) != CSP_ERR_NONE) { + csp_close(conn); + return NULL; + } + } +#endif + + /* We have a successful connection */ + return conn; + +} + +inline int csp_conn_dport(csp_conn_t * conn) { + + return conn->idin.dport; + +} + +inline int csp_conn_sport(csp_conn_t * conn) { + + return conn->idin.sport; + +} + +inline int csp_conn_dst(csp_conn_t * conn) { + + return conn->idin.dst; + +} + +inline int csp_conn_src(csp_conn_t * conn) { + + return conn->idin.src; + +} + +inline int csp_conn_flags(csp_conn_t * conn) { + + return conn->idin.flags; + +} + +#ifdef CSP_DEBUG +void csp_conn_print_table(void) { + + int i; + csp_conn_t * conn; + + for (i = 0; i < CSP_CONN_MAX; i++) { + conn = &arr_conn[i]; + printf("[%02u %p] S:%u, %u -> %u, %u -> %u, sock: %p\r\n", + i, conn, conn->state, conn->idin.src, conn->idin.dst, + conn->idin.dport, conn->idin.sport, conn->socket); +#ifdef CSP_USE_RDP + if (conn->idin.flags & CSP_FRDP) + csp_rdp_conn_print(conn); +#endif + } +} + +int csp_conn_print_table_str(char * str_buf, int str_size) { + + int i, start = 0; + csp_conn_t * conn; + char buf[100]; + + /* Display up to 10 connections */ + if (CSP_CONN_MAX - 10 > 0) + start = CSP_CONN_MAX - 10; + + for (i = start; i < CSP_CONN_MAX; i++) { + conn = &arr_conn[i]; + snprintf(buf, sizeof(buf), "[%02u %p] S:%u, %u -> %u, %u -> %u, sock: %p\n", + i, conn, conn->state, conn->idin.src, conn->idin.dst, + conn->idin.dport, conn->idin.sport, conn->socket); + + strncat(str_buf, buf, str_size); + if ((str_size -= strlen(buf)) <= 0) + break; + } + + return CSP_ERR_NONE; +} +#endif + +const csp_conn_t * csp_conn_get_array(size_t * size) +{ + *size = CSP_CONN_MAX; + return arr_conn; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_conn.h b/gomspace/libgscsp/lib/libcsp/src/csp_conn.h new file mode 100644 index 00000000..3fa0ff52 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_conn.h @@ -0,0 +1,112 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_CONN_H_ +#define _CSP_CONN_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include +#include + +/** @brief Connection states */ +typedef enum { + CONN_CLOSED = 0, + CONN_OPEN = 1, +} csp_conn_state_t; + +/** @brief Connection types */ +typedef enum { + CONN_CLIENT = 0, + CONN_SERVER = 1, +} csp_conn_type_t; + +typedef enum { + RDP_CLOSED = 0, + RDP_SYN_SENT, + RDP_SYN_RCVD, + RDP_OPEN, + RDP_CLOSE_WAIT, +} csp_rdp_state_t; + +/** @brief RDP Connection header + * @note Do not try to pack this struct, the posix sem handle will stop working */ +typedef struct { + csp_rdp_state_t state; /**< Connection state */ + uint16_t snd_nxt; /**< The sequence number of the next segment that is to be sent */ + uint16_t snd_una; /**< The sequence number of the oldest unacknowledged segment */ + uint16_t snd_iss; /**< The initial send sequence number */ + uint16_t rcv_cur; /**< The sequence number of the last segment received correctly and in sequence */ + uint16_t rcv_irs; /**< The initial receive sequence number */ + uint16_t rcv_lsa; /**< The last sequence number acknowledged by the receiver */ + uint32_t window_size; + uint32_t conn_timeout; + uint32_t packet_timeout; + uint32_t delayed_acks; + uint32_t ack_timeout; + uint32_t ack_delay_count; + uint32_t ack_timestamp; + csp_bin_sem_handle_t tx_wait; + csp_queue_handle_t tx_queue; + csp_queue_handle_t rx_queue; +} csp_rdp_t; + +/** @brief Connection struct */ +struct csp_conn_s { + csp_conn_type_t type; /* Connection type (CONN_CLIENT or CONN_SERVER) */ + csp_conn_state_t state; /* Connection state (CONN_OPEN or CONN_CLOSED) */ + csp_mutex_t lock; /* Connection structure lock */ + csp_id_t idin; /* Identifier received */ + csp_id_t idout; /* Identifier transmitted */ +#ifdef CSP_USE_QOS + csp_queue_handle_t rx_event; /* Event queue for RX packets */ +#endif + csp_queue_handle_t rx_queue[CSP_RX_QUEUES]; /* Queue for RX packets */ + csp_queue_handle_t socket; /* Socket to be "woken" when first packet is ready */ + uint32_t timestamp; /* Time the connection was opened */ + uint32_t opts; /* Connection or socket options */ +#ifdef CSP_USE_RDP + csp_rdp_t rdp; /* RDP state */ +#endif +}; + +int csp_conn_lock(csp_conn_t * conn, uint32_t timeout); +int csp_conn_unlock(csp_conn_t * conn); +int csp_conn_enqueue_packet(csp_conn_t * conn, csp_packet_t * packet); +int csp_conn_init(void); +csp_conn_t * csp_conn_allocate(csp_conn_type_t type); +csp_conn_t * csp_conn_find(uint32_t id, uint32_t mask); +csp_conn_t * csp_conn_new(csp_id_t idin, csp_id_t idout); +void csp_conn_check_timeouts(void); +int csp_conn_get_rxq(int prio); + +const csp_conn_t * csp_conn_get_array(size_t * size); // for test purposes only! + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_CONN_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_crc32.c b/gomspace/libgscsp/lib/libcsp/src/csp_crc32.c new file mode 100644 index 00000000..8bf2145f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_crc32.c @@ -0,0 +1,140 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#include +#include + +#include + +#ifdef CSP_USE_CRC32 + +#ifdef __AVR__ +#include +static const uint32_t crc_tab[256] PROGMEM = { +#else +static const uint32_t crc_tab[256] = { +#endif + 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, + 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, + 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, + 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, + 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, + 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, + 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, + 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, + 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, + 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, + 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, + 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, + 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, + 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, + 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, + 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, + 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, + 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, + 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, + 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, + 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, + 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, + 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, + 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, + 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, + 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, + 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, + 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, + 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, + 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, + 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, + 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 }; + +uint32_t csp_crc32_memory(const uint8_t * data, uint32_t length) { + uint32_t crc; + + crc = 0xFFFFFFFF; + while (length--) +#ifdef __AVR__ + crc = pgm_read_dword(&crc_tab[(crc ^ *data++) & 0xFFL]) ^ (crc >> 8); +#else + crc = crc_tab[(crc ^ *data++) & 0xFFL] ^ (crc >> 8); +#endif + + return (crc ^ 0xFFFFFFFF); +} + +int csp_crc32_append(csp_packet_t * packet, bool include_header) { + + uint32_t crc; + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + /* Calculate CRC32, convert to network byte order */ + if (include_header) { + crc = csp_crc32_memory((uint8_t *) &packet->id, packet->length + sizeof(packet->id)); + } else { + crc = csp_crc32_memory(packet->data, packet->length); + } + crc = csp_hton32(crc); + + /* Copy checksum to packet */ + memcpy(&packet->data[packet->length], &crc, sizeof(uint32_t)); + packet->length += sizeof(uint32_t); + + return CSP_ERR_NONE; + +} + +int csp_crc32_verify(csp_packet_t * packet, bool include_header) { + + uint32_t crc; + + /* NULL pointer check */ + if (packet == NULL) + return CSP_ERR_INVAL; + + if (packet->length < sizeof(uint32_t)) + return CSP_ERR_INVAL; + + /* Calculate CRC32, convert to network byte order */ + if (include_header) { + crc = csp_crc32_memory((uint8_t *) &packet->id, packet->length + sizeof(packet->id) - sizeof(uint32_t)); + } else { + crc = csp_crc32_memory(packet->data, packet->length - sizeof(uint32_t)); + } + crc = csp_hton32(crc); + + /* Compare calculated checksum with packet header */ + if (memcmp(&packet->data[packet->length] - sizeof(uint32_t), &crc, sizeof(uint32_t)) != 0) { + /* CRC32 failed */ + return CSP_ERR_INVAL; + } else { + /* Strip CRC32 */ + packet->length -= sizeof(uint32_t); + return CSP_ERR_NONE; + } + +} + +#endif // CSP_USE_CRC32 diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_debug.c b/gomspace/libgscsp/lib/libcsp/src/csp_debug.c new file mode 100644 index 00000000..2e710cb3 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_debug.c @@ -0,0 +1,133 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#ifdef __AVR__ +#include +#endif + +/* CSP includes */ +#include + +#include + +/* Custom debug function */ +csp_debug_hook_func_t csp_debug_hook_func = NULL; + +/* Debug levels */ +static bool csp_debug_level_enabled[] = { + [CSP_ERROR] = true, + [CSP_WARN] = true, + [CSP_INFO] = false, + [CSP_BUFFER] = false, + [CSP_PACKET] = false, + [CSP_PROTOCOL] = false, + [CSP_LOCK] = false, +}; + +/* Some compilers do not support weak symbols, so this function + * can be used instead to set a custom debug hook */ +void csp_debug_hook_set(csp_debug_hook_func_t f) +{ + csp_debug_hook_func = f; +} + +void do_csp_debug(csp_debug_level_t level, const char *format, ...) +{ + int color = COLOR_RESET; + va_list args; + + /* Don't print anything if log level is disabled */ + if (level > CSP_LOCK || !csp_debug_level_enabled[level]) + return; + + switch(level) { + case CSP_INFO: + color = COLOR_GREEN | COLOR_BOLD; + break; + case CSP_ERROR: + color = COLOR_RED | COLOR_BOLD; + break; + case CSP_WARN: + color = COLOR_YELLOW | COLOR_BOLD; + break; + case CSP_BUFFER: + color = COLOR_MAGENTA; + break; + case CSP_PACKET: + color = COLOR_GREEN; + break; + case CSP_PROTOCOL: + color = COLOR_BLUE; + break; + case CSP_LOCK: + color = COLOR_CYAN; + break; + default: + return; + } + + va_start(args, format); + + /* If csp_debug_hook symbol is defined, pass on the message. + * Otherwise, just print with pretty colors ... */ + if (csp_debug_hook_func) { + csp_debug_hook_func(level, format, args); + } else { + csp_sys_set_color(color); +#ifdef __AVR__ + vfprintf_P(stdout, format, args); +#else + vprintf(format, args); +#endif + printf("\r\n"); + csp_sys_set_color(COLOR_RESET); + } + + va_end(args); +} + +void csp_debug_set_level(csp_debug_level_t level, bool value) +{ + if (level > CSP_LOCK) + return; + csp_debug_level_enabled[level] = value; +} + +int csp_debug_get_level(csp_debug_level_t level) +{ + if (level > CSP_LOCK) + return 0; + return csp_debug_level_enabled[level]; +} + +void csp_debug_toggle_level(csp_debug_level_t level) +{ + if (level > CSP_LOCK) { + printf("Max level is 6\r\n"); + return; + } + csp_debug_level_enabled[level] = (csp_debug_level_enabled[level]) ? false : true; + printf("Level %u: value %u\r\n", level, csp_debug_level_enabled[level]); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_dedup.c b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.c new file mode 100644 index 00000000..d263c7a4 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.c @@ -0,0 +1,66 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include "csp_dedup.h" + +/* Check the last CSP_DEDUP_COUNT packets for duplicates */ +#define CSP_DEDUP_COUNT 16 + +/* Only consider packet a duplicate if received under CSP_DEDUP_WINDOW_MS ago */ +#define CSP_DEDUP_WINDOW_MS 1000 + +/* Store packet CRC's in a ringbuffer */ +static uint32_t csp_dedup_array[CSP_DEDUP_COUNT] = {}; +static uint32_t csp_dedup_timestamp[CSP_DEDUP_COUNT] = {}; +static int csp_dedup_in = 0; + +bool csp_dedup_is_duplicate(csp_packet_t *packet) +{ + /* Calculate CRC32 for packet */ + uint32_t crc = csp_crc32_memory((const uint8_t *) &packet->id, packet->length + sizeof(packet->id)); + + /* Check if we have received this packet before */ + for (int i = 0; i < CSP_DEDUP_COUNT; i++) { + + /* Check for match */ + if (crc == csp_dedup_array[i]) { + + /* Check the timestamp */ + if (csp_get_ms() < csp_dedup_timestamp[i] + CSP_DEDUP_WINDOW_MS) + return true; + } + } + + /* If not, insert packet into duplicate list */ + csp_dedup_array[csp_dedup_in] = crc; + csp_dedup_timestamp[csp_dedup_in] = csp_get_ms(); + csp_dedup_in = (csp_dedup_in + 1) % CSP_DEDUP_COUNT; + + return false; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_dedup.h b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.h new file mode 100644 index 00000000..75a3f124 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_dedup.h @@ -0,0 +1,31 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_DEDUP_H_ +#define CSP_DEDUP_H_ + +/** + * Check for a duplicate packet + * @param packet pointer to packet + * @return false if not a duplicate, true if duplicate + */ +bool csp_dedup_is_duplicate(csp_packet_t *packet); + +#endif /* CSP_DEDUP_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_endian.c b/gomspace/libgscsp/lib/libcsp/src/csp_endian.c new file mode 100644 index 00000000..6d0ef226 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_endian.c @@ -0,0 +1,204 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* CSP includes */ +#include +#include + +/* Convert 16-bit number from host byte order to network byte order */ +inline uint16_t __attribute__ ((__const__)) csp_hton16(uint16_t h16) { +#ifdef CSP_BIG_ENDIAN + return h16; +#else + return (((h16 & 0xff00) >> 8) | + ((h16 & 0x00ff) << 8)); +#endif +} + +/* Convert 16-bit number from network byte order to host byte order */ +inline uint16_t __attribute__ ((__const__)) csp_ntoh16(uint16_t n16) { + return csp_hton16(n16); +} + +/* Convert 32-bit number from host byte order to network byte order */ +inline uint32_t __attribute__ ((__const__)) csp_hton32(uint32_t h32) { +#ifdef CSP_BIG_ENDIAN + return h32; +#else + return (((h32 & 0xff000000) >> 24) | + ((h32 & 0x000000ff) << 24) | + ((h32 & 0x0000ff00) << 8) | + ((h32 & 0x00ff0000) >> 8)); +#endif +} + +/* Convert 32-bit number from network byte order to host byte order */ +inline uint32_t __attribute__ ((__const__)) csp_ntoh32(uint32_t n32) { + return csp_hton32(n32); +} + +/* Convert 64-bit number from host byte order to network byte order */ +inline uint64_t __attribute__ ((__const__)) csp_hton64(uint64_t h64) { +#ifdef CSP_BIG_ENDIAN + return h64; +#else + return (((h64 & 0xff00000000000000LL) >> 56) | + ((h64 & 0x00000000000000ffLL) << 56) | + ((h64 & 0x00ff000000000000LL) >> 40) | + ((h64 & 0x000000000000ff00LL) << 40) | + ((h64 & 0x0000ff0000000000LL) >> 24) | + ((h64 & 0x0000000000ff0000LL) << 24) | + ((h64 & 0x000000ff00000000LL) >> 8) | + ((h64 & 0x00000000ff000000LL) << 8)); +#endif +} + +/* Convert 64-bit number from host byte order to network byte order */ +inline uint64_t __attribute__ ((__const__)) csp_ntoh64(uint64_t n64) { + return csp_hton64(n64); +} + +/* Convert 16-bit number from host byte order to big endian byte order */ +inline uint16_t __attribute__ ((__const__)) csp_htobe16(uint16_t h16) { + return csp_hton16(h16); +} + +/* Convert 16-bit number from host byte order to little endian byte order */ +inline uint16_t __attribute__ ((__const__)) csp_htole16(uint16_t h16) { +#ifdef CSP_LITTLE_ENDIAN + return h16; +#else + return (((h16 & 0xff00) >> 8) | + ((h16 & 0x00ff) << 8)); +#endif +} + +/* Convert 16-bit number from big endian byte order to little endian byte order */ +inline uint16_t __attribute__ ((__const__)) csp_betoh16(uint16_t be16) { + return csp_ntoh16(be16); +} + +/* Convert 16-bit number from little endian byte order to host byte order */ +inline uint16_t __attribute__ ((__const__)) csp_letoh16(uint16_t le16) { + return csp_htole16(le16); +} + +/* Convert 32-bit number from host byte order to big endian byte order */ +inline uint32_t __attribute__ ((__const__)) csp_htobe32(uint32_t h32) { + return csp_hton32(h32); +} + +/* Convert 32-bit number from little endian byte order to host byte order */ +inline uint32_t __attribute__ ((__const__)) csp_htole32(uint32_t h32) { +#ifdef CSP_LITTLE_ENDIAN + return h32; +#else + return (((h32 & 0xff000000) >> 24) | + ((h32 & 0x000000ff) << 24) | + ((h32 & 0x0000ff00) << 8) | + ((h32 & 0x00ff0000) >> 8)); +#endif +} + +/* Convert 32-bit number from big endian byte order to host byte order */ +inline uint32_t __attribute__ ((__const__)) csp_betoh32(uint32_t be32) { + return csp_ntoh32(be32); +} + +/* Convert 32-bit number from little endian byte order to host byte order */ +inline uint32_t __attribute__ ((__const__)) csp_letoh32(uint32_t le32) { + return csp_htole32(le32); +} + +/* Convert 64-bit number from host byte order to big endian byte order */ +inline uint64_t __attribute__ ((__const__)) csp_htobe64(uint64_t h64) { + return csp_hton64(h64); +} + +/* Convert 64-bit number from host byte order to little endian byte order */ +inline uint64_t __attribute__ ((__const__)) csp_htole64(uint64_t h64) { +#ifdef CSP_LITTLE_ENDIAN + return h64; +#else + return (((h64 & 0xff00000000000000LL) >> 56) | + ((h64 & 0x00000000000000ffLL) << 56) | + ((h64 & 0x00ff000000000000LL) >> 40) | + ((h64 & 0x000000000000ff00LL) << 40) | + ((h64 & 0x0000ff0000000000LL) >> 24) | + ((h64 & 0x0000000000ff0000LL) << 24) | + ((h64 & 0x000000ff00000000LL) >> 8) | + ((h64 & 0x00000000ff000000LL) << 8)); +#endif +} + +/* Convert 64-bit number from big endian byte order to host byte order */ +inline uint64_t __attribute__ ((__const__)) csp_betoh64(uint64_t be64) { + return csp_ntoh64(be64); +} + +/* Convert 64-bit number from little endian byte order to host byte order */ +inline uint64_t __attribute__ ((__const__)) csp_letoh64(uint64_t le64) { + return csp_htole64(le64); +} + + +/* Convert float from host byte order to network byte order */ +inline float __attribute__ ((__const__)) csp_htonflt(float f) { +#ifdef CSP_BIG_ENDIAN + return f; +#else + union v { + float f; + uint32_t i; + }; + union v val; + val.f = f; + val.i = csp_hton32(val.i); + return val.f; +#endif +} + +/* Convert float from host byte order to network byte order */ +inline float __attribute__ ((__const__)) csp_ntohflt(float f) { + return csp_htonflt(f); +} + +/* Convert double from host byte order to network byte order */ +inline double __attribute__ ((__const__)) csp_htondbl(double d) { +#ifdef CSP_BIG_ENDIAN + return d; +#else + union v { + double d; + uint64_t i; + }; + union v val; + val.d = d; + val.i = csp_hton64(val.i); + return val.d; +#endif +} + +/* Convert float from host byte order to network byte order */ +inline double __attribute__ ((__const__)) csp_ntohdbl(double d) { + return csp_htondbl(d); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_hex_dump.c b/gomspace/libgscsp/lib/libcsp/src/csp_hex_dump.c new file mode 100644 index 00000000..af0a2660 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_hex_dump.c @@ -0,0 +1,55 @@ +#include +#include + +void csp_hex_dump (const char *desc, void *addr, int len) +{ + int i; + unsigned char buff[17]; + unsigned char *pc = (unsigned char*)addr; + + // Output description if given. + if (desc != NULL) + printf ("%s:\r\n", desc); + + if (len == 0) { + printf(" ZERO LENGTH\r\n"); + return; + } + if (len < 0) { + printf(" NEGATIVE LENGTH: %i\r\n",len); + return; + } + + // Process every byte in the data. + for (i = 0; i < len; i++) { + // Multiple of 16 means new line (with line offset). + + if ((i % 16) == 0) { + // Just don't print ASCII for the zeroth line. + if (i != 0) + printf (" %s\r\n", buff); + + // Output the offset. + printf (" %p ", addr + i); + } + + // Now the hex code for the specific character. + printf (" %02x", pc[i]); + + // And store a printable ASCII character for later. + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + // Pad out last line if not exactly 16 characters. + while ((i % 16) != 0) { + printf (" "); + i++; + } + + // And print the final ASCII bit. + printf (" %s\r\n", buff); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_iflist.c b/gomspace/libgscsp/lib/libcsp/src/csp_iflist.c new file mode 100644 index 00000000..2bfef422 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_iflist.c @@ -0,0 +1,100 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +/* Interfaces are stored in a linked list*/ +static csp_iface_t * interfaces = NULL; + +csp_iface_t * csp_iflist_get_by_name(const char *name) { + csp_iface_t *ifc = interfaces; + while(ifc) { + if (strncmp(ifc->name, name, 10) == 0) + break; + ifc = ifc->next; + } + return ifc; +} + +void csp_iflist_add(csp_iface_t *ifc) { + + /* Add interface to pool */ + if (interfaces == NULL) { + /* This is the first interface to be added */ + interfaces = ifc; + ifc->next = NULL; + } else { + /* One or more interfaces were already added */ + csp_iface_t * i = interfaces; + while (i != ifc && i->next) + i = i->next; + + /* Insert interface last if not already in pool */ + if (i != ifc && i->next == NULL) { + i->next = ifc; + ifc->next = NULL; + } + } + +} + +csp_iface_t * csp_iflist_get(void) +{ + return interfaces; +} + +#ifdef CSP_DEBUG +static int csp_bytesize(char *buf, int len, unsigned long int n) { + char postfix; + double size; + + if (n >= 1048576) { + size = n/1048576.0; + postfix = 'M'; + } else if (n >= 1024) { + size = n/1024.; + postfix = 'K'; + } else { + size = n; + postfix = 'B'; + } + + return snprintf(buf, len, "%.1f%c", size, postfix); +} + +void csp_iflist_print(void) { + csp_iface_t * i = interfaces; + char txbuf[25], rxbuf[25]; + + while (i) { + csp_bytesize(txbuf, 25, i->txbytes); + csp_bytesize(rxbuf, 25, i->rxbytes); + printf("%-5s tx: %05"PRIu32" rx: %05"PRIu32" txe: %05"PRIu32" rxe: %05"PRIu32"\r\n" + " drop: %05"PRIu32" autherr: %05"PRIu32 " frame: %05"PRIu32"\r\n" + " txb: %"PRIu32" (%s) rxb: %"PRIu32" (%s)\r\n\r\n", + i->name, i->tx, i->rx, i->tx_error, i->rx_error, i->drop, + i->autherr, i->frame, i->txbytes, txbuf, i->rxbytes, rxbuf); + i = i->next; + } + +} +#endif + diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_io.c b/gomspace/libgscsp/lib/libcsp/src/csp_io.c new file mode 100644 index 00000000..3d7f614a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_io.c @@ -0,0 +1,502 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "csp_io.h" +#include "csp_port.h" +#include "csp_conn.h" +#include "csp_route.h" +#include "csp_promisc.h" +#include "csp_qfifo.h" +#include "transport/csp_transport.h" + +/** CSP address of this node */ +static uint8_t csp_my_address; + +/* Hostname, model and build revision */ +static const char *csp_hostname = NULL; +static const char *csp_model = NULL; +static const char *csp_revision = GIT_REV; + +#ifdef CSP_USE_PROMISC +extern csp_queue_handle_t csp_promisc_queue; +#endif + +void csp_set_address(uint8_t addr) +{ + csp_my_address = addr; +} + +uint8_t csp_get_address(void) +{ + return csp_my_address; +} + +void csp_set_hostname(const char *hostname) +{ + csp_hostname = hostname; +} + +const char *csp_get_hostname(void) +{ + return csp_hostname; +} + +void csp_set_model(const char *model) +{ + csp_model = model; +} + +const char *csp_get_model(void) +{ + return csp_model; +} + +void csp_set_revision(const char *revision) +{ + csp_revision = revision; +} + +const char *csp_get_revision(void) +{ + return csp_revision; +} + +int csp_init(unsigned char address) { + + int ret; + + /* Initialize CSP */ + csp_set_address(address); + + ret = csp_conn_init(); + if (ret != CSP_ERR_NONE) + return ret; + + ret = csp_port_init(); + if (ret != CSP_ERR_NONE) + return ret; + + ret = csp_qfifo_init(); + if (ret != CSP_ERR_NONE) + return ret; + + /* Loopback */ + csp_iflist_add(&csp_if_lo); + + /* Register loopback route */ + csp_route_set(csp_get_address(), &csp_if_lo, CSP_NODE_MAC); + + /* Also register loopback as default, until user redefines default route */ + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_lo, CSP_NODE_MAC); + + return CSP_ERR_NONE; + +} + +csp_socket_t * csp_socket(uint32_t opts) { + + /* Validate socket options */ +#ifndef CSP_USE_RDP + if (opts & CSP_SO_RDPREQ) { + csp_log_error("Attempt to create socket that requires RDP, but CSP was compiled without RDP support"); + return NULL; + } +#endif + +#ifndef CSP_USE_XTEA + if (opts & CSP_SO_XTEAREQ) { + csp_log_error("Attempt to create socket that requires XTEA, but CSP was compiled without XTEA support"); + return NULL; + } +#endif + +#ifndef CSP_USE_HMAC + if (opts & CSP_SO_HMACREQ) { + csp_log_error("Attempt to create socket that requires HMAC, but CSP was compiled without HMAC support"); + return NULL; + } +#endif + +#ifndef CSP_USE_CRC32 + if (opts & CSP_SO_CRC32REQ) { + csp_log_error("Attempt to create socket that requires CRC32, but CSP was compiled without CRC32 support"); + return NULL; + } +#endif + + /* Drop packet if reserved flags are set */ + if (opts & ~(CSP_SO_RDPREQ | CSP_SO_XTEAREQ | CSP_SO_HMACREQ | CSP_SO_CRC32REQ | CSP_SO_CONN_LESS)) { + csp_log_error("Invalid socket option"); + return NULL; + } + + /* Use CSP buffers instead? */ + csp_socket_t * sock = csp_conn_allocate(CONN_SERVER); + if (sock == NULL) + return NULL; + + /* If connectionless, init the queue to a pre-defined size + * if not, the user must init the queue using csp_listen */ + if (opts & CSP_SO_CONN_LESS) { + sock->socket = csp_queue_create(CSP_CONN_QUEUE_LENGTH, sizeof(csp_packet_t *)); + if (sock->socket == NULL) { + csp_close(sock); + return NULL; + } + } else { + sock->socket = NULL; + } + sock->opts = opts; + + return sock; + +} + +csp_conn_t * csp_accept(csp_socket_t * sock, uint32_t timeout) { + + if (sock == NULL) + return NULL; + + if (sock->socket == NULL) + return NULL; + + csp_conn_t * conn; + if (csp_queue_dequeue(sock->socket, &conn, timeout) == CSP_QUEUE_OK) + return conn; + + return NULL; + +} + +csp_packet_t * csp_read(csp_conn_t * conn, uint32_t timeout) { + + csp_packet_t * packet = NULL; + + if (conn == NULL || conn->state != CONN_OPEN) + return NULL; + +#ifdef CSP_USE_QOS + int prio, event; + if (csp_queue_dequeue(conn->rx_event, &event, timeout) != CSP_QUEUE_OK) + return NULL; + + for (prio = 0; prio < CSP_RX_QUEUES; prio++) + if (csp_queue_dequeue(conn->rx_queue[prio], &packet, 0) == CSP_QUEUE_OK) + break; +#else + if (csp_queue_dequeue(conn->rx_queue[0], &packet, timeout) != CSP_QUEUE_OK) + return NULL; +#endif + +#ifdef CSP_USE_RDP + /* Packet read could trigger ACK transmission */ + if (conn->idin.flags & CSP_FRDP && conn->rdp.delayed_acks) + csp_rdp_check_ack(conn); + +#endif + + return packet; + +} + +int csp_send_direct(csp_id_t idout, csp_packet_t * packet, csp_iface_t * ifout, uint32_t timeout) { + + if (packet == NULL) { + csp_log_error("csp_send_direct called with NULL packet"); + goto err; + } + + if ((ifout == NULL) || (ifout->nexthop == NULL)) { + csp_log_error("No route to host: %#08x", idout.ext); + goto err; + } + + csp_log_packet("OUT: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %u VIA: %s", + idout.src, idout.dst, idout.dport, idout.sport, idout.pri, idout.flags, packet->length, ifout->name); + + /* Copy identifier to packet (before crc, xtea and hmac) */ + packet->id.ext = idout.ext; + +#ifdef CSP_USE_PROMISC + /* Loopback traffic is added to promisc queue by the router */ + if (idout.dst != csp_get_address() && idout.src == csp_get_address()) { + packet->id.ext = idout.ext; + csp_promisc_add(packet); + } +#endif + + /* Only encrypt packets from the current node */ + if (idout.src == csp_get_address()) { + /* Append HMAC */ + if (idout.flags & CSP_FHMAC) { +#ifdef CSP_USE_HMAC + /* Calculate and add HMAC (does not include header for backwards compatability with csp1.x) */ + if (csp_hmac_append(packet, false) != 0) { + /* HMAC append failed */ + csp_log_warn("HMAC append failed!"); + goto tx_err; + } +#else + csp_log_warn("Attempt to send packet with HMAC, but CSP was compiled without HMAC support. Discarding packet"); + goto tx_err; +#endif + } + + /* Append CRC32 */ + if (idout.flags & CSP_FCRC32) { +#ifdef CSP_USE_CRC32 + /* Calculate and add CRC32 (does not include header for backwards compatability with csp1.x) */ + if (csp_crc32_append(packet, false) != 0) { + /* CRC32 append failed */ + csp_log_warn("CRC32 append failed!"); + goto tx_err; + } +#else + csp_log_warn("Attempt to send packet with CRC32, but CSP was compiled without CRC32 support. Sending without CRC32r"); + idout.flags &= ~(CSP_FCRC32); +#endif + } + + if (idout.flags & CSP_FXTEA) { +#ifdef CSP_USE_XTEA + /* Create nonce */ + uint32_t nonce, nonce_n; + nonce = (uint32_t)rand(); + nonce_n = csp_hton32(nonce); + memcpy(&packet->data[packet->length], &nonce_n, sizeof(nonce_n)); + + /* Create initialization vector */ + uint32_t iv[2] = {nonce, 1}; + + /* Encrypt data */ + if (csp_xtea_encrypt(packet->data, packet->length, iv) != 0) { + /* Encryption failed */ + csp_log_warn("Encryption failed! Discarding packet"); + goto tx_err; + } + + packet->length += sizeof(nonce_n); +#else + csp_log_warn("Attempt to send XTEA encrypted packet, but CSP was compiled without XTEA support. Discarding packet"); + goto tx_err; +#endif + } + } + + /* Store length before passing to interface */ + uint16_t bytes = packet->length; + uint16_t mtu = ifout->mtu; + + if (mtu > 0 && bytes > mtu) + goto tx_err; + + if ((*ifout->nexthop)(ifout, packet, timeout) != CSP_ERR_NONE) + goto tx_err; + + ifout->tx++; + ifout->txbytes += bytes; + return CSP_ERR_NONE; + +tx_err: + ifout->tx_error++; +err: + return CSP_ERR_TX; + +} + +int csp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) { + + int ret; + + if ((conn == NULL) || (packet == NULL) || (conn->state != CONN_OPEN)) { + csp_log_error("Invalid call to csp_send"); + return 0; + } + +#ifdef CSP_USE_RDP + if (conn->idout.flags & CSP_FRDP) { + if (csp_rdp_send(conn, packet, timeout) != CSP_ERR_NONE) { + csp_iface_t * ifout = csp_rtable_find_iface(conn->idout.dst); + if (ifout != NULL) + ifout->tx_error++; + csp_log_warn("RDP send failed!"); + return 0; + } + } +#endif + + csp_iface_t * ifout = csp_rtable_find_iface(conn->idout.dst); + ret = csp_send_direct(conn->idout, packet, ifout, timeout); + + return (ret == CSP_ERR_NONE) ? 1 : 0; + +} + +int csp_send_prio(uint8_t prio, csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) { + conn->idout.pri = prio; + return csp_send(conn, packet, timeout); +} + +int csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen) { + + int size = (inlen > outlen) ? inlen : outlen; + csp_packet_t * packet = csp_buffer_get(size); + if (packet == NULL) + return 0; + + /* Copy the request */ + if (outlen > 0 && outbuf != NULL) + memcpy(packet->data, outbuf, outlen); + packet->length = outlen; + + if (!csp_send(conn, packet, timeout)) { + csp_buffer_free(packet); + return 0; + } + + /* If no reply is expected, return now */ + if (inlen == 0) + return 1; + + packet = csp_read(conn, timeout); + if (packet == NULL) + return 0; + + if ((inlen != -1) && ((int)packet->length != inlen)) { + csp_log_error("Reply length %u expected %u", packet->length, inlen); + csp_buffer_free(packet); + return 0; + } + + memcpy(inbuf, packet->data, packet->length); + int length = packet->length; + csp_buffer_free(packet); + return length; + +} + +int csp_transaction(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, void * outbuf, int outlen, void * inbuf, int inlen) { + return csp_transaction2(prio, dest, port, timeout, outbuf, outlen, inbuf, inlen, 0); +} + +int csp_transaction2(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, 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) + return 0; + + int status = csp_transaction_persistent(conn, timeout, outbuf, outlen, inbuf, inlen); + + csp_close(conn); + + return status; + +} + +csp_packet_t * csp_recvfrom(csp_socket_t * socket, uint32_t timeout) { + + if ((socket == NULL) || (!(socket->opts & CSP_SO_CONN_LESS))) + return NULL; + + csp_packet_t * packet = NULL; + csp_queue_dequeue(socket->socket, &packet, timeout); + + return packet; + +} + +int csp_sendto(uint8_t prio, uint8_t dest, uint8_t dport, uint8_t src_port, uint32_t opts, csp_packet_t * packet, uint32_t timeout) { + + packet->id.flags = 0; + + if (opts & CSP_O_RDP) { + csp_log_error("Attempt to create RDP packet on connection-less socket"); + return CSP_ERR_INVAL; + } + + if (opts & CSP_O_HMAC) { +#ifdef CSP_USE_HMAC + packet->id.flags |= CSP_FHMAC; +#else + csp_log_error("Attempt to create HMAC authenticated packet, but CSP was compiled without HMAC support"); + return CSP_ERR_NOTSUP; +#endif + } + + if (opts & CSP_O_XTEA) { +#ifdef CSP_USE_XTEA + packet->id.flags |= CSP_FXTEA; +#else + csp_log_error("Attempt to create XTEA encrypted packet, but CSP was compiled without XTEA support"); + return CSP_ERR_NOTSUP; +#endif + } + + if (opts & CSP_O_CRC32) { +#ifdef CSP_USE_CRC32 + packet->id.flags |= CSP_FCRC32; +#else + csp_log_error("Attempt to create CRC32 validated packet, but CSP was compiled without CRC32 support"); + return CSP_ERR_NOTSUP; +#endif + } + + packet->id.dst = dest; + packet->id.dport = dport; + packet->id.src = csp_get_address(); + packet->id.sport = src_port; + packet->id.pri = prio; + + csp_iface_t * ifout = csp_rtable_find_iface(dest); + if (csp_send_direct(packet->id, packet, ifout, timeout) != CSP_ERR_NONE) + return CSP_ERR_NOTSUP; + + return CSP_ERR_NONE; + +} + +int csp_sendto_reply(csp_packet_t * request_packet, csp_packet_t * reply_packet, uint32_t opts, uint32_t timeout) { + if (request_packet == NULL) + return CSP_ERR_INVAL; + + return csp_sendto(request_packet->id.pri, request_packet->id.src, request_packet->id.sport, request_packet->id.dport, opts, reply_packet, timeout); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_io.h b/gomspace/libgscsp/lib/libcsp/src/csp_io.h new file mode 100644 index 00000000..6ea8dfec --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_io.h @@ -0,0 +1,47 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_IO_H_ +#define _CSP_IO_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** + * Function to transmit a frame without an existing connection structure. + * This function is used for stateless transmissions + * @param idout 32bit CSP identifier + * @param packet pointer to packet, + * @param ifout pointer to output interface + * @param timeout a timeout to wait for TX to complete. NOTE: not all underlying drivers supports flow-control. + * @return returns 1 if successful and 0 otherwise. you MUST free the frame yourself if the transmission was not successful. + */ +int csp_send_direct(csp_id_t idout, csp_packet_t * packet, csp_iface_t * ifout, uint32_t timeout); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_IO_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_port.c b/gomspace/libgscsp/lib/libcsp/src/csp_port.c new file mode 100644 index 00000000..2a4ac2a9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_port.c @@ -0,0 +1,105 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include + +#include +#include +#include + +#include "csp_port.h" +#include "csp_conn.h" + +/* Allocation of ports */ +static csp_port_t ports[CSP_MAX_BIND_PORT + 2]; + +csp_socket_t * csp_port_get_socket(unsigned int port) { + + csp_socket_t * ret = NULL; + + if (port >= CSP_ANY) + return NULL; + + /* Match dport to socket or local "catch all" port number */ + if (ports[port].state == PORT_OPEN) + ret = ports[port].socket; + else if (ports[CSP_ANY].state == PORT_OPEN) + ret = ports[CSP_ANY].socket; + + return ret; + +} + +int csp_port_init(void) { + + memset(ports, PORT_CLOSED, sizeof(csp_port_t) * (CSP_MAX_BIND_PORT + 2)); + + return CSP_ERR_NONE; + +} + +int csp_listen(csp_socket_t * socket, size_t conn_queue_length) { + + if (socket == NULL) + return CSP_ERR_INVAL; + + socket->socket = csp_queue_create(conn_queue_length, sizeof(csp_conn_t *)); + if (socket->socket == NULL) + return CSP_ERR_NOMEM; + + socket->opts |= CSP_SO_INTERNAL_LISTEN; + + return CSP_ERR_NONE; + +} + +int csp_bind(csp_socket_t * socket, uint8_t port) { + + if (socket == NULL) + return CSP_ERR_INVAL; + + if (port > CSP_ANY) { + csp_log_error("Only ports from 0-%u (and CSP_ANY for default) are available for incoming ports", CSP_ANY); + return CSP_ERR_INVAL; + } + + /* Check if port number is valid */ + if (ports[port].state != PORT_CLOSED) { + csp_log_error("Port %d is already in use", port); + return CSP_ERR_USED; + } + + csp_log_info("Binding socket %p to port %u", socket, port); + + /* Save listener */ + ports[port].socket = socket; + ports[port].state = PORT_OPEN; + + return CSP_ERR_NONE; + +} + + diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_port.h b/gomspace/libgscsp/lib/libcsp/src/csp_port.h new file mode 100644 index 00000000..d2ec06e9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_port.h @@ -0,0 +1,55 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_PORT_H_ +#define _CSP_PORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +/** @brief Port states */ +typedef enum { + PORT_CLOSED = 0, + PORT_OPEN = 1, +} csp_port_state_t; + +/** @brief Port struct */ +typedef struct { + csp_port_state_t state; // Port state + csp_socket_t * socket; // New connections are added to this socket's conn queue +} csp_port_t; + +/** + * Init ports array + */ +int csp_port_init(void); + +csp_socket_t * csp_port_get_socket(unsigned int dport); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // _CSP_PORT_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_promisc.c b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.c new file mode 100644 index 00000000..5f156c33 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.c @@ -0,0 +1,82 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#ifdef CSP_USE_PROMISC + +static csp_queue_handle_t csp_promisc_queue = NULL; +static int csp_promisc_enabled = 0; + +int csp_promisc_enable(unsigned int buf_size) { + + /* If queue already initialised */ + if (csp_promisc_queue != NULL) { + csp_promisc_enabled = 1; + return CSP_ERR_NONE; + } + + /* Create packet queue */ + csp_promisc_queue = csp_queue_create(buf_size, sizeof(csp_packet_t *)); + + if (csp_promisc_queue == NULL) + return CSP_ERR_INVAL; + + csp_promisc_enabled = 1; + return CSP_ERR_NONE; + +} + +void csp_promisc_disable(void) { + csp_promisc_enabled = 0; +} + +csp_packet_t * csp_promisc_read(uint32_t timeout) { + + if (csp_promisc_queue == NULL) + return NULL; + + csp_packet_t * packet = NULL; + csp_queue_dequeue(csp_promisc_queue, &packet, timeout); + + return packet; + +} + +void csp_promisc_add(csp_packet_t * packet) { + + if (csp_promisc_enabled == 0) + return; + + if (csp_promisc_queue != NULL) { + /* Make a copy of the message and queue it to the promiscuous task */ + csp_packet_t *packet_copy = csp_buffer_clone(packet); + if (packet_copy != NULL) { + if (csp_queue_enqueue(csp_promisc_queue, &packet_copy, 0) != CSP_QUEUE_OK) { + csp_log_error("Promiscuous mode input queue full"); + csp_buffer_free(packet_copy); + } + } + } + +} + +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_promisc.h b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.h new file mode 100644 index 00000000..be62edda --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_promisc.h @@ -0,0 +1,30 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 Gomspace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_PROMISC_H_ +#define CSP_PROMISC_H_ + +/** + * Add packet to promiscuous mode packet queue + * @param packet Packet to add to the queue + */ +void csp_promisc_add(csp_packet_t * packet); + +#endif /* CSP_PROMISC_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.c b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.c new file mode 100644 index 00000000..9329b2ca --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.c @@ -0,0 +1,149 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include "csp_qfifo.h" + +static csp_queue_handle_t qfifo[CSP_ROUTE_FIFOS]; +#ifdef CSP_USE_QOS +static csp_queue_handle_t qfifo_events; +#endif + +int csp_qfifo_init(void) { + int prio; + + /* Create router fifos for each priority */ + for (prio = 0; prio < CSP_ROUTE_FIFOS; prio++) { + if (qfifo[prio] == NULL) { + qfifo[prio] = csp_queue_create(CSP_FIFO_INPUT, sizeof(csp_qfifo_t)); + if (!qfifo[prio]) + return CSP_ERR_NOMEM; + } + } + +#ifdef CSP_USE_QOS + /* Create QoS fifo notification queue */ + qfifo_events = csp_queue_create(CSP_FIFO_INPUT, sizeof(int)); + if (!qfifo_events) + return CSP_ERR_NOMEM; +#endif + + return CSP_ERR_NONE; + +} + +int csp_qfifo_read(csp_qfifo_t * input) { + +#ifdef CSP_USE_QOS + int prio, found, event; + + /* Wait for packet in any queue */ + if (csp_queue_dequeue(qfifo_events, &event, FIFO_TIMEOUT) != CSP_QUEUE_OK) + return CSP_ERR_TIMEDOUT; + + /* Find packet with highest priority */ + found = 0; + for (prio = 0; prio < CSP_ROUTE_FIFOS; prio++) { + if (csp_queue_dequeue(qfifo[prio], input, 0) == CSP_QUEUE_OK) { + found = 1; + break; + } + } + + if (!found) { + csp_log_warn("Spurious wakeup: No packet found"); + return CSP_ERR_TIMEDOUT; + } +#else + if (csp_queue_dequeue(qfifo[0], input, FIFO_TIMEOUT) != CSP_QUEUE_OK) + return CSP_ERR_TIMEDOUT; +#endif + + return CSP_ERR_NONE; + +} + +void csp_qfifo_write(csp_packet_t * packet, csp_iface_t * interface, CSP_BASE_TYPE * pxTaskWoken) { + + int result; + + if (packet == NULL) { + if (pxTaskWoken == NULL) { // Only do logging in non-ISR context + csp_log_warn("csp_new packet called with NULL packet"); + } + return; + } else if (interface == NULL) { + if (pxTaskWoken == NULL) { // Only do logging in non-ISR context + csp_log_warn("csp_new packet called with NULL interface"); + } + if (pxTaskWoken == NULL) + csp_buffer_free(packet); + else + csp_buffer_free_isr(packet); + return; + } + + csp_qfifo_t queue_element; + queue_element.interface = interface; + queue_element.packet = packet; + +#ifdef CSP_USE_QOS + int fifo = packet->id.pri; +#else + int fifo = 0; +#endif + + if (pxTaskWoken == NULL) + result = csp_queue_enqueue(qfifo[fifo], &queue_element, 0); + else + result = csp_queue_enqueue_isr(qfifo[fifo], &queue_element, pxTaskWoken); + +#ifdef CSP_USE_QOS + static int event = 0; + + if (result == CSP_QUEUE_OK) { + if (pxTaskWoken == NULL) + csp_queue_enqueue(qfifo_events, &event, 0); + else + csp_queue_enqueue_isr(qfifo_events, &event, pxTaskWoken); + } +#endif + + if (result != CSP_QUEUE_OK) { + if (pxTaskWoken == NULL) { // Only do logging in non-ISR context + csp_log_warn("ERROR: Routing input FIFO is FULL. Dropping packet."); + } + interface->drop++; + if (pxTaskWoken == NULL) + csp_buffer_free(packet); + else + csp_buffer_free_isr(packet); + } + +} + +void csp_qfifo_wake_up(void) { + csp_qfifo_t queue_element; + queue_element.interface = NULL; + queue_element.packet = NULL; + csp_queue_enqueue(qfifo[0], &queue_element, 0); +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.h b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.h new file mode 100644 index 00000000..2910c48d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_qfifo.h @@ -0,0 +1,54 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef CSP_QFIFO_H_ +#define CSP_QFIFO_H_ + +#ifdef 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_DELAY //! If no RDP, the router can sleep untill data arrives +#endif + +/** + * Init FIFO/QOS queues + * @return CSP_ERR type + */ +int csp_qfifo_init(void); + +typedef struct { + csp_iface_t * interface; + csp_packet_t * packet; +} csp_qfifo_t; + +/** + * Read next packet from router input queue + * @param input pointer to router queue item element + * @return CSP_ERR type + */ +int csp_qfifo_read(csp_qfifo_t * input); + +/** + * Wake up any task (e.g. router) waiting on messages. + * For testing. + */ +void csp_qfifo_wake_up(void); + +#endif /* CSP_QFIFO_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_route.c b/gomspace/libgscsp/lib/libcsp/src/csp_route.c new file mode 100644 index 00000000..bc843577 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_route.c @@ -0,0 +1,346 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include +#include + +#include +#include + +#include +#include + +#include "csp_port.h" +#include "csp_conn.h" +#include "csp_io.h" +#include "csp_promisc.h" +#include "csp_qfifo.h" +#include "csp_dedup.h" +#include "transport/csp_transport.h" + +/** + * Check supported packet options + * @param interface pointer to incoming interface + * @param packet pointer to packet + * @return CSP_ERR_NONE is all options are supported, CSP_ERR_NOTSUP if not + */ +static int csp_route_check_options(csp_iface_t *interface, csp_packet_t *packet) +{ +#ifndef CSP_USE_XTEA + /* Drop XTEA packets */ + if (packet->id.flags & CSP_FXTEA) { + csp_log_error("Received XTEA encrypted packet, but CSP was compiled without XTEA support. Discarding packet"); + interface->autherr++; + return CSP_ERR_NOTSUP; + } +#endif + +#ifndef CSP_USE_HMAC + /* Drop HMAC packets */ + if (packet->id.flags & CSP_FHMAC) { + csp_log_error("Received packet with HMAC, but CSP was compiled without HMAC support. Discarding packet"); + interface->autherr++; + return CSP_ERR_NOTSUP; + } +#endif + +#ifndef CSP_USE_RDP + /* Drop RDP packets */ + if (packet->id.flags & CSP_FRDP) { + csp_log_error("Received RDP packet, but CSP was compiled without RDP support. Discarding packet"); + interface->rx_error++; + return CSP_ERR_NOTSUP; + } +#endif + return CSP_ERR_NONE; +} + +/** + * Helper function to decrypt, check auth and CRC32 + * @param security_opts either socket_opts or conn_opts + * @param interface pointer to incoming interface + * @param packet pointer to packet + * @return -1 Missing feature, -2 XTEA error, -3 CRC error, -4 HMAC error, 0 = OK. + */ +static int csp_route_security_check(uint32_t security_opts, csp_iface_t * interface, csp_packet_t * packet) { + +#ifdef CSP_USE_XTEA + /* XTEA encrypted packet */ + if (packet->id.flags & CSP_FXTEA) { + /* Read nonce */ + uint32_t nonce; + memcpy(&nonce, &packet->data[packet->length - sizeof(nonce)], sizeof(nonce)); + nonce = csp_ntoh32(nonce); + packet->length -= sizeof(nonce); + + /* Create initialization vector */ + uint32_t iv[2] = {nonce, 1}; + + /* Decrypt data */ + if (csp_xtea_decrypt(packet->data, packet->length, iv) != 0) { + /* Decryption failed */ + csp_log_error("Decryption failed! Discarding packet"); + interface->autherr++; + return CSP_ERR_XTEA; + } + } else if (security_opts & CSP_SO_XTEAREQ) { + csp_log_warn("Received packet without XTEA encryption. Discarding packet"); + interface->autherr++; + return CSP_ERR_XTEA; + } +#endif + + /* CRC32 verified packet */ + if (packet->id.flags & CSP_FCRC32) { +#ifdef CSP_USE_CRC32 + if (packet->length < 4) + csp_log_error("Too short packet for CRC32, %u", packet->length); + /* Verify CRC32 (does not include header for backwards compatability with csp1.x) */ + if (csp_crc32_verify(packet, false) != 0) { + /* Checksum failed */ + csp_log_error("CRC32 verification error! Discarding packet"); + interface->rx_error++; + return CSP_ERR_CRC32; + } + } else if (security_opts & CSP_SO_CRC32REQ) { + csp_log_warn("Received packet without CRC32. Accepting packet"); +#else + /* Strip CRC32 field and accept the packet */ + csp_log_warn("Received packet with CRC32, but CSP was compiled without CRC32 support. Accepting packet"); + packet->length -= sizeof(uint32_t); +#endif + } + +#ifdef CSP_USE_HMAC + /* HMAC authenticated packet */ + if (packet->id.flags & CSP_FHMAC) { + /* Verify HMAC (does not include header for backwards compatability with csp1.x) */ + if (csp_hmac_verify(packet, false) != 0) { + /* HMAC failed */ + csp_log_error("HMAC verification error! Discarding packet"); + interface->autherr++; + return CSP_ERR_HMAC; + } + } else if (security_opts & CSP_SO_HMACREQ) { + csp_log_warn("Received packet without HMAC. Discarding packet"); + interface->autherr++; + return CSP_ERR_HMAC; + } +#endif + +#ifdef CSP_USE_RDP + /* RDP packet */ + if (!(packet->id.flags & CSP_FRDP)) { + if (security_opts & CSP_SO_RDPREQ) { + csp_log_warn("Received packet without RDP header. Discarding packet"); + interface->rx_error++; + return CSP_ERR_INVAL; + } + } +#endif + + return CSP_ERR_NONE; + +} + +int csp_route_work(uint32_t timeout) { + + csp_qfifo_t input; + csp_packet_t * packet; + csp_conn_t * conn; + csp_socket_t * socket; + +#ifdef CSP_USE_RDP + /* Check connection timeouts (currently only for RDP) */ + csp_conn_check_timeouts(); +#endif + + /* Get next packet to route */ + if (csp_qfifo_read(&input) != CSP_ERR_NONE) + return -1; + + packet = input.packet; + if (!packet) + return -1; + + csp_log_packet("INP: S %u, D %u, Dp %u, Sp %u, Pr %u, Fl 0x%02X, Sz %"PRIu16" VIA: %s", + packet->id.src, packet->id.dst, packet->id.dport, + packet->id.sport, packet->id.pri, packet->id.flags, packet->length, input.interface->name); + + /* Here there be promiscuous mode */ +#ifdef CSP_USE_PROMISC + csp_promisc_add(packet); +#endif + +#ifdef CSP_USE_DEDUP + /* Check for duplicates */ + if (csp_dedup_is_duplicate(packet)) { + /* Discard packet */ + csp_log_packet("Duplicate packet discarded"); + input.interface->drop++; + csp_buffer_free(packet); + return 0; + } +#endif + + /* Now we count the message (since its deduplicated) */ + input.interface->rx++; + input.interface->rxbytes += packet->length; + + /* If the message is not to me, route the message to the correct interface */ + if ((packet->id.dst != csp_get_address()) && (packet->id.dst != CSP_BROADCAST_ADDR)) { + + /* Find the destination interface */ + csp_iface_t * dstif = csp_rtable_find_iface(packet->id.dst); + + /* If the message resolves to the input interface, don't loop it back out */ + if ((dstif == NULL) || ((dstif == input.interface) && (input.interface->split_horizon_off == 0))) { + csp_buffer_free(packet); + return 0; + } + + /* Otherwise, actually send the message */ + if (csp_send_direct(packet->id, packet, dstif, 0) != CSP_ERR_NONE) { + csp_log_warn("Router failed to send"); + csp_buffer_free(packet); + } + + /* Next message, please */ + return 0; + } + + /* Discard packets with unsupported options */ + if (csp_route_check_options(input.interface, packet) != CSP_ERR_NONE) { + csp_buffer_free(packet); + return 0; + } + + /* The message is to me, search for incoming socket */ + socket = csp_port_get_socket(packet->id.dport); + + /* If the socket is connection-less, deliver now */ + if (socket && (socket->opts & CSP_SO_CONN_LESS)) { + if (csp_route_security_check(socket->opts, input.interface, packet) < 0) { + csp_buffer_free(packet); + return 0; + } + if (csp_queue_enqueue(socket->socket, &packet, 0) != CSP_QUEUE_OK) { + csp_log_error("Conn-less socket queue full"); + csp_buffer_free(packet); + return 0; + } + return 0; + } + + /* Search for an existing connection */ + conn = csp_conn_find(packet->id.ext, CSP_ID_CONN_MASK); + + /* If this is an incoming packet on a new connection */ + if (conn == NULL) { + + /* Reject packet if no matching socket is found */ + if (!socket) { + csp_buffer_free(packet); + return 0; + } + + /* Run security check on incoming packet */ + if (csp_route_security_check(socket->opts, input.interface, packet) < 0) { + csp_buffer_free(packet); + return 0; + } + + /* New incoming connection accepted */ + csp_id_t idout; + idout.pri = packet->id.pri; + idout.src = csp_get_address(); + + idout.dst = packet->id.src; + idout.dport = packet->id.sport; + idout.sport = packet->id.dport; + idout.flags = packet->id.flags; + + /* Create connection */ + conn = csp_conn_new(packet->id, idout); + + if (!conn) { + csp_log_error("No more connections available"); + csp_buffer_free(packet); + return 0; + } + + /* Store the socket queue and options */ + conn->socket = socket->socket; + conn->opts = socket->opts; + + /* Packet to existing connection */ + } else { + + /* Run security check on incoming packet */ + if (csp_route_security_check(conn->opts, input.interface, packet) < 0) { + csp_buffer_free(packet); + return 0; + } + + } + +#ifdef CSP_USE_RDP + /* Pass packet to RDP module */ + if (packet->id.flags & CSP_FRDP) { + csp_rdp_new_packet(conn, packet); + return 0; + } +#endif + + /* Pass packet to UDP module */ + csp_udp_new_packet(conn, packet); + return 0; +} + +static CSP_DEFINE_TASK(csp_task_router) { + + /* Here there be routing */ + while (1) { + csp_route_work(FIFO_TIMEOUT); + } + + return CSP_TASK_RETURN; + +} + +int csp_route_start_task(unsigned int task_stack_size, unsigned int priority) { + + static csp_thread_handle_t handle_router; + int ret = csp_thread_create(csp_task_router, "RTE", task_stack_size, NULL, priority, &handle_router); + + if (ret != 0) { + csp_log_error("Failed to start router task"); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_route.h b/gomspace/libgscsp/lib/libcsp/src/csp_route.h new file mode 100644 index 00000000..2a20f49f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_route.h @@ -0,0 +1,24 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_ROUTE_H_ +#define _CSP_ROUTE_H_ + +#endif // _CSP_ROUTE_H_ diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_service_handler.c b/gomspace/libgscsp/lib/libcsp/src/csp_service_handler.c new file mode 100644 index 00000000..0090afc1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_service_handler.c @@ -0,0 +1,334 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* CSP includes */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "csp_route.h" + +#define CSP_RPS_MTU 196 + +/** + * The CSP CMP mempy function is used to, override the function used to + * read/write memory by peek and poke. + */ +#ifdef __AVR__ +static uint32_t wrap_32bit_memcpy(uint32_t to, const uint32_t from, size_t size) { + return (uint32_t) (uintptr_t) memcpy((void *) (uintptr_t) to, (const void *) (uintptr_t) from, 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; +#endif + + +void csp_cmp_set_memcpy(csp_memcpy_fnc_t fnc) { + csp_cmp_memcpy_fnc = fnc; +} + +static int do_cmp_ident(struct csp_cmp_message *cmp) { + + /* Copy revision */ + strncpy(cmp->ident.revision, csp_get_revision(), CSP_CMP_IDENT_REV_LEN); + cmp->ident.revision[CSP_CMP_IDENT_REV_LEN - 1] = '\0'; + + /* Copy compilation date */ + strncpy(cmp->ident.date, __DATE__, CSP_CMP_IDENT_DATE_LEN); + cmp->ident.date[CSP_CMP_IDENT_DATE_LEN - 1] = '\0'; + + /* Copy compilation time */ + strncpy(cmp->ident.time, __TIME__, CSP_CMP_IDENT_TIME_LEN); + cmp->ident.time[CSP_CMP_IDENT_TIME_LEN - 1] = '\0'; + + /* Copy hostname */ + strncpy(cmp->ident.hostname, csp_get_hostname(), CSP_HOSTNAME_LEN); + cmp->ident.hostname[CSP_HOSTNAME_LEN - 1] = '\0'; + + /* Copy model name */ + strncpy(cmp->ident.model, csp_get_model(), CSP_MODEL_LEN); + cmp->ident.model[CSP_MODEL_LEN - 1] = '\0'; + + return CSP_ERR_NONE; + +} + +static int do_cmp_route_set(struct csp_cmp_message *cmp) { + + csp_iface_t *ifc = csp_iflist_get_by_name(cmp->route_set.interface); + if (ifc == NULL) + return CSP_ERR_INVAL; + + if (csp_route_set(cmp->route_set.dest_node, ifc, cmp->route_set.next_hop_mac) != CSP_ERR_NONE) + return CSP_ERR_INVAL; + + return CSP_ERR_NONE; + +} + +static int do_cmp_if_stats(struct csp_cmp_message *cmp) { + + csp_iface_t *ifc = csp_iflist_get_by_name(cmp->if_stats.interface); + if (ifc == NULL) + return CSP_ERR_INVAL; + + cmp->if_stats.tx = csp_hton32(ifc->tx); + cmp->if_stats.rx = csp_hton32(ifc->rx); + cmp->if_stats.tx_error = csp_hton32(ifc->tx_error); + cmp->if_stats.rx_error = csp_hton32(ifc->rx_error); + cmp->if_stats.drop = csp_hton32(ifc->drop); + cmp->if_stats.autherr = csp_hton32(ifc->autherr); + cmp->if_stats.frame = csp_hton32(ifc->frame); + cmp->if_stats.txbytes = csp_hton32(ifc->txbytes); + cmp->if_stats.rxbytes = csp_hton32(ifc->rxbytes); + cmp->if_stats.irq = csp_hton32(ifc->irq); + + return CSP_ERR_NONE; +} + +static int do_cmp_peek(struct csp_cmp_message *cmp) { + + cmp->peek.addr = csp_hton32(cmp->peek.addr); + if (cmp->peek.len > CSP_CMP_PEEK_MAX_LEN) + return CSP_ERR_INVAL; + + /* Dangerous, you better know what you are doing */ + csp_cmp_memcpy_fnc((csp_memptr_t) (uintptr_t) cmp->peek.data, (csp_memptr_t) (unsigned long) cmp->peek.addr, cmp->peek.len); + + return CSP_ERR_NONE; + +} + +static int do_cmp_poke(struct csp_cmp_message *cmp) { + + cmp->poke.addr = csp_hton32(cmp->poke.addr); + if (cmp->poke.len > CSP_CMP_POKE_MAX_LEN) + return CSP_ERR_INVAL; + + /* Extremely dangerous, you better know what you are doing */ + csp_cmp_memcpy_fnc((csp_memptr_t) (unsigned long) cmp->poke.addr, (csp_memptr_t) (uintptr_t) cmp->poke.data, cmp->poke.len); + + return CSP_ERR_NONE; + +} + +static int do_cmp_clock(struct csp_cmp_message *cmp) { + + cmp->clock.tv_sec = csp_ntoh32(cmp->clock.tv_sec); + cmp->clock.tv_nsec = csp_ntoh32(cmp->clock.tv_nsec); + + if ((cmp->clock.tv_sec != 0) && (clock_set_time != NULL)) { + clock_set_time(&cmp->clock); + } + + if (clock_get_time != NULL) { + clock_get_time(&cmp->clock); + } + + cmp->clock.tv_sec = csp_hton32(cmp->clock.tv_sec); + cmp->clock.tv_nsec = csp_hton32(cmp->clock.tv_nsec); + return CSP_ERR_NONE; + +} + +/* CSP Management Protocol handler */ +static int csp_cmp_handler(csp_conn_t * conn, csp_packet_t * packet) { + + int ret = CSP_ERR_INVAL; + struct csp_cmp_message * cmp = (struct csp_cmp_message *) packet->data; + + /* Ignore everything but requests */ + if (cmp->type != CSP_CMP_REQUEST) + return ret; + + switch (cmp->code) { + case CSP_CMP_IDENT: + ret = do_cmp_ident(cmp); + packet->length = CMP_SIZE(ident); + break; + + case CSP_CMP_ROUTE_SET: + ret = do_cmp_route_set(cmp); + packet->length = CMP_SIZE(route_set); + break; + + case CSP_CMP_IF_STATS: + ret = do_cmp_if_stats(cmp); + packet->length = CMP_SIZE(if_stats); + break; + + case CSP_CMP_PEEK: + ret = do_cmp_peek(cmp); + break; + + case CSP_CMP_POKE: + ret = do_cmp_poke(cmp); + break; + + case CSP_CMP_CLOCK: + ret = do_cmp_clock(cmp); + break; + + default: + ret = CSP_ERR_INVAL; + break; + } + + cmp->type = CSP_CMP_REPLY; + + return ret; +} + +void csp_service_handler(csp_conn_t * conn, csp_packet_t * packet) { + + switch (csp_conn_dport(conn)) { + + case CSP_CMP: + /* Pass to CMP handler */ + if (csp_cmp_handler(conn, packet) != CSP_ERR_NONE) { + csp_buffer_free(packet); + return; + } + break; + + case CSP_PING: + /* A ping means, just echo the packet, so no changes */ + csp_log_info("SERVICE: Ping received"); + break; + + case CSP_PS: { + /* Sanity check on request */ + if ((packet->length != 1) || (packet->data[0] != 0x55)) { + /* Sanity check failed */ + csp_buffer_free(packet); + /* Clear the packet, it has been freed */ + packet = NULL; + break; + } + /* Start by allocating just the right amount of memory */ + int task_list_size = csp_sys_tasklist_size(); + char * pslist = csp_malloc(task_list_size); + /* Check for malloc fail */ + if (pslist == NULL) { + /* Send out the data */ + strcpy((char *)packet->data, "Not enough memory"); + packet->length = strlen((char *)packet->data); + /* Break and let the default handling send packet */ + break; + } + + /* Retrieve the tasklist */ + csp_sys_tasklist(pslist); + int pslen = strnlen(pslist, task_list_size); + + /* Split the potentially very long string into packets */ + int i = 0; + while(i < pslen) { + + /* Allocate packet buffer, if need be */ + if (packet == NULL) + packet = csp_buffer_get(CSP_RPS_MTU); + if (packet == NULL) + break; + + /* Calculate length, either full MTU or the remainder */ + packet->length = (pslen - i > CSP_RPS_MTU) ? CSP_RPS_MTU : (pslen - i); + + /* Send out the data */ + memcpy(packet->data, &pslist[i], packet->length); + i += packet->length; + if (!csp_send(conn, packet, 0)) + csp_buffer_free(packet); + + /* Clear the packet reference when sent */ + packet = NULL; + + } + csp_free(pslist); + break; + } + + case CSP_MEMFREE: { + uint32_t total = csp_sys_memfree(); + + total = csp_hton32(total); + memcpy(packet->data, &total, sizeof(total)); + packet->length = sizeof(total); + + break; + } + + case CSP_REBOOT: { + uint32_t magic_word; + memcpy(&magic_word, packet->data, sizeof(magic_word)); + + magic_word = csp_ntoh32(magic_word); + + /* If the magic word is valid, reboot */ + if (magic_word == CSP_REBOOT_MAGIC) { + csp_sys_reboot(); + } else if (magic_word == CSP_REBOOT_SHUTDOWN_MAGIC) { + csp_sys_shutdown(); + } + + + + csp_buffer_free(packet); + return; + } + + case CSP_BUF_FREE: { + uint32_t size = csp_buffer_remaining(); + size = csp_hton32(size); + memcpy(packet->data, &size, sizeof(size)); + packet->length = sizeof(size); + break; + } + + case CSP_UPTIME: { + uint32_t time = csp_get_s(); + time = csp_hton32(time); + memcpy(packet->data, &time, sizeof(time)); + packet->length = sizeof(time); + break; + } + + default: + csp_buffer_free(packet); + return; + } + + if (packet != NULL) { + if (!csp_send(conn, packet, 0)) + csp_buffer_free(packet); + } + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_services.c b/gomspace/libgscsp/lib/libcsp/src/csp_services.c new file mode 100644 index 00000000..5392cb82 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_services.c @@ -0,0 +1,233 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +/* CSP includes */ +#include +#include +#include + +#include + +int csp_ping(uint8_t node, uint32_t timeout, unsigned int size, uint8_t conn_options) { + + unsigned int i; + uint32_t start, time, status = 0; + + /* Counter */ + start = csp_get_ms(); + + /* Open connection */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, node, CSP_PING, timeout, conn_options); + if (conn == NULL) + return -1; + + /* Prepare data */ + csp_packet_t * packet; + packet = csp_buffer_get(size); + if (packet == NULL) + goto out; + + /* Set data to increasing numbers */ + packet->length = size; + for (i = 0; i < size; i++) + packet->data[i] = i; + + /* Try to send frame */ + if (!csp_send(conn, packet, 0)) + goto out; + + /* Read incoming frame */ + packet = csp_read(conn, timeout); + if (packet == NULL) + goto out; + + /* Ensure that the data was actually echoed */ + for (i = 0; i < size; i++) + if (packet->data[i] != i % (0xff + 1)) + goto out; + + status = 1; + +out: + /* Clean up */ + if (packet != NULL) + csp_buffer_free(packet); + csp_close(conn); + + /* We have a reply */ + time = (csp_get_ms() - start); + + if (status) { + return time; + } else { + return -1; + } + +} + +void csp_ping_noreply(uint8_t node) { + + /* Prepare data */ + csp_packet_t * packet; + packet = csp_buffer_get(1); + + /* Check malloc */ + if (packet == NULL) + return; + + /* Open connection */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, node, CSP_PING, 0, 0); + if (conn == NULL) { + csp_buffer_free(packet); + return; + } + + packet->data[0] = 0x55; + packet->length = 1; + + printf("Ping ignore reply node %u.\r\n", (unsigned int) node); + + /* Try to send frame */ + if (!csp_send(conn, packet, 0)) + csp_buffer_free(packet); + + csp_close(conn); + +} + +void csp_reboot(uint8_t node) { + uint32_t magic_word = csp_hton32(CSP_REBOOT_MAGIC); + csp_transaction(CSP_PRIO_NORM, node, CSP_REBOOT, 0, &magic_word, sizeof(magic_word), NULL, 0); +} + +void csp_shutdown(uint8_t node) { + uint32_t magic_word = csp_hton32(CSP_REBOOT_SHUTDOWN_MAGIC); + csp_transaction(CSP_PRIO_NORM, node, CSP_REBOOT, 0, &magic_word, sizeof(magic_word), NULL, 0); +} + +void csp_ps(uint8_t node, uint32_t timeout) { + + /* Open connection */ + csp_conn_t * conn = csp_connect(CSP_PRIO_NORM, node, CSP_PS, 0, 0); + if (conn == NULL) + return; + + /* Prepare data */ + csp_packet_t * packet; + packet = csp_buffer_get(95); + + /* Check malloc */ + if (packet == NULL) + goto out; + + packet->data[0] = 0x55; + packet->length = 1; + + printf("PS node %u: \r\n", (unsigned int) node); + + /* Try to send frame */ + if (!csp_send(conn, packet, 0)) + goto out; + + while(1) { + + /* Read incoming frame */ + packet = csp_read(conn, timeout); + if (packet == NULL) + break; + + /* We have a reply, add our own NULL char */ + packet->data[packet->length] = 0; + printf("%s", packet->data); + + /* Each packet from csp_read must to be freed by user */ + csp_buffer_free(packet); + } + + printf("\r\n"); + + /* Clean up */ +out: + if (packet != NULL) + csp_buffer_free(packet); + csp_close(conn); + +} + +void csp_memfree(uint8_t node, uint32_t timeout) { + + uint32_t memfree; + + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_MEMFREE, timeout, NULL, 0, &memfree, sizeof(memfree)); + if (status == 0) { + printf("Network error\r\n"); + return; + } + + /* Convert from network to host order */ + memfree = csp_ntoh32(memfree); + + printf("Free Memory at node %u is %"PRIu32" bytes\r\n", (unsigned int) node, memfree); + +} + +void csp_buf_free(uint8_t node, uint32_t timeout) { + + uint32_t size = 0; + + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_BUF_FREE, timeout, NULL, 0, &size, sizeof(size)); + if (status == 0) { + printf("Network error\r\n"); + return; + } + size = csp_ntoh32(size); + printf("Free buffers at node %u is %"PRIu32"\r\n", (unsigned int) node, size); + +} + +void csp_uptime(uint8_t node, uint32_t timeout) { + + uint32_t uptime = 0; + + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_UPTIME, timeout, NULL, 0, &uptime, sizeof(uptime)); + if (status == 0) { + printf("Network error\r\n"); + return; + } + uptime = csp_ntoh32(uptime); + printf("Uptime of node %u is %"PRIu32" s\r\n", (unsigned int) node, uptime); + +} + +int csp_cmp(uint8_t node, uint32_t timeout, uint8_t code, int membsize, struct csp_cmp_message * msg) { + msg->type = CSP_CMP_REQUEST; + msg->code = code; + int status = csp_transaction(CSP_PRIO_NORM, node, CSP_CMP, timeout, msg, membsize, msg, membsize); + if (status == 0) + return CSP_ERR_TIMEDOUT; + + return CSP_ERR_NONE; +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/csp_sfp.c b/gomspace/libgscsp/lib/libcsp/src/csp_sfp.c new file mode 100644 index 00000000..96ef36e1 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/csp_sfp.c @@ -0,0 +1,170 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include "csp_conn.h" + +typedef struct __attribute__((__packed__)) { + uint32_t offset; + uint32_t totalsize; +} sfp_header_t; + +/** + * SFP Headers: + * The following functions are helper functions that handles the extra SFP + * information that needs to be appended to all data packets. + */ +static sfp_header_t * csp_sfp_header_add(csp_packet_t * packet) { + sfp_header_t * header = (sfp_header_t *) &packet->data[packet->length]; + packet->length += sizeof(sfp_header_t); + memset(header, 0, sizeof(sfp_header_t)); + return header; +} + +static sfp_header_t * csp_sfp_header_remove(csp_packet_t * packet) { + sfp_header_t * header = (sfp_header_t *) &packet->data[packet->length-sizeof(sfp_header_t)]; + packet->length -= sizeof(sfp_header_t); + return header; +} + +int csp_sfp_send_own_memcpy(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout, void * (*memcpyfcn)(void *, const void *, size_t)) { + + int count = 0; + while(count < totalsize) { + + /* Allocate packet */ + csp_packet_t * packet = csp_buffer_get(mtu); + if (packet == NULL) + return -1; + + /* Calculate sending size */ + int size = totalsize - count; + if (size > mtu) + size = mtu; + + /* Print debug */ + csp_debug(CSP_PROTOCOL, "Sending SFP at %x size %u", data + count, size); + + /* Copy data */ + (*memcpyfcn)(packet->data, data + count, size); + packet->length = size; + + /* Set fragment flag */ + conn->idout.flags |= CSP_FFRAG; + + /* Add SFP header */ + sfp_header_t * sfp_header = csp_sfp_header_add(packet); + sfp_header->totalsize = csp_hton32(totalsize); + sfp_header->offset = csp_hton32(count); + + /* Send data */ + if (!csp_send(conn, packet, timeout)) { + csp_buffer_free(packet); + return -1; + } + + /* Increment count */ + count += size; + + } + + return 0; + +} + +int csp_sfp_send(csp_conn_t * conn, const void * data, int totalsize, int mtu, uint32_t timeout) { + return csp_sfp_send_own_memcpy(conn, data, totalsize, mtu, timeout, &memcpy); +} + +int csp_sfp_recv_fp(csp_conn_t * conn, void ** dataout, int * datasize, uint32_t timeout, csp_packet_t * first_packet) { + + unsigned int last_byte = 0; + + *dataout = NULL; /* Allow caller to assume csp_free() can always be called when dataout is non-NULL */ + + /* Get first packet from user, or from connection */ + csp_packet_t * packet = NULL; + if (first_packet == NULL) { + packet = csp_read(conn, timeout); + if (packet == NULL) + return -1; + } else { + packet = first_packet; + } + + do { + + /* Check that SFP header is present */ + if ((packet->id.flags & CSP_FFRAG) == 0) { + csp_debug(CSP_ERROR, "Missing SFP header"); + csp_buffer_free(packet); + return -1; + } + + /* Read SFP header */ + sfp_header_t * sfp_header = csp_sfp_header_remove(packet); + sfp_header->offset = csp_ntoh32(sfp_header->offset); + sfp_header->totalsize = csp_ntoh32(sfp_header->totalsize); + + csp_debug(CSP_PROTOCOL, "SFP fragment %u/%u", sfp_header->offset + packet->length, sfp_header->totalsize); + + if (sfp_header->offset > last_byte + 1) { + csp_debug(CSP_ERROR, "SFP missing %u bytes", sfp_header->offset - last_byte); + csp_buffer_free(packet); + return -1; + } else { + last_byte = sfp_header->offset + packet->length; + } + + /* Allocate memory */ + if (*dataout == NULL) + *dataout = csp_malloc(sfp_header->totalsize); + if (*dataout == NULL) { + csp_debug(CSP_ERROR, "No dyn-memory for SFP fragment"); + csp_buffer_free(packet); + return -1; + } + + /* Copy data to output */ + *datasize = sfp_header->totalsize; + memcpy(*dataout + sfp_header->offset, packet->data, packet->length); + + if (sfp_header->offset + packet->length >= sfp_header->totalsize) { + csp_debug(CSP_PROTOCOL, "SFP complete"); + csp_buffer_free(packet); + return 0; + } else { + csp_buffer_free(packet); + } + + } while((packet = csp_read(conn, timeout)) != NULL); + + return -1; + +} + +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); +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/drivers/can/can_socketcan.c b/gomspace/libgscsp/lib/libcsp/src/drivers/can/can_socketcan.c new file mode 100644 index 00000000..7d12f184 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/drivers/can/can_socketcan.c @@ -0,0 +1,201 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* SocketCAN driver */ +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef CSP_HAVE_LIBSOCKETCAN +#include +#endif + +static struct can_socketcan_s { + int socket; + csp_iface_t interface; +} socketcan[1] = { + { + .interface = { + .name = "CAN", + .nexthop = csp_can_tx, + .mtu = CSP_CAN_MTU, + .driver = &socketcan[0], + }, + }, +}; + +static void * socketcan_rx_thread(void * parameters) +{ + struct can_frame frame; + int nbytes; + + while (1) { + /* Read CAN frame */ + nbytes = read(socketcan[0].socket, &frame, sizeof(frame)); + if (nbytes < 0) { + csp_log_error("read: %s", strerror(errno)); + continue; + } + + if (nbytes != sizeof(frame)) { + csp_log_warn("Read incomplete CAN frame"); + continue; + } + + /* Frame type */ + if (frame.can_id & (CAN_ERR_FLAG | CAN_RTR_FLAG) || !(frame.can_id & CAN_EFF_FLAG)) { + /* Drop error and remote frames */ + csp_log_warn("Discarding ERR/RTR/SFF frame"); + continue; + } + + /* Strip flags */ + frame.can_id &= CAN_EFF_MASK; + + /* Call RX callbacsp_can_rx_frameck */ + csp_can_rx(&socketcan[0].interface, frame.can_id, frame.data, frame.can_dlc, NULL); + } + + /* We should never reach this point */ + pthread_exit(NULL); +} + + +int csp_can_tx_frame(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc) +{ + struct can_frame frame; + int i, tries = 0; + memset(&frame, 0, sizeof(frame)); + if (dlc > 8) + return -1; + + /* Copy identifier */ + frame.can_id = id | CAN_EFF_FLAG; + + /* Copy data to frame */ + for (i = 0; i < dlc; i++) + frame.data[i] = data[i]; + + /* Set DLC */ + frame.can_dlc = dlc; + + /* Send frame */ + while (write(socketcan[0].socket, &frame, sizeof(frame)) != sizeof(frame)) { + if (++tries < 1000 && errno == ENOBUFS) { + /* Wait 10 ms and try again */ + usleep(10000); + } else { + csp_log_error("write: %s", strerror(errno)); + break; + } + } + + return 0; +} + +csp_iface_t * csp_can_socketcan_init(const char * ifc, int bitrate, int promisc) +{ + struct ifreq ifr; + struct sockaddr_can addr; + pthread_t rx_thread; + + printf("Init can interface %s\n", ifc); + +#ifdef CSP_HAVE_LIBSOCKETCAN + /* Set interface up */ + if (bitrate > 0) { + can_do_stop(ifc); + can_set_bitrate(ifc, bitrate); + can_set_restart_ms(ifc, 100); + can_do_start(ifc); + } +#endif + + /* Create socket */ + if ((socketcan[0].socket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { + csp_log_error("socket: %s", strerror(errno)); + return NULL; + } + + /* Locate interface */ + strncpy(ifr.ifr_name, ifc, IFNAMSIZ - 1); + if (ioctl(socketcan[0].socket, SIOCGIFINDEX, &ifr) < 0) { + csp_log_error("ioctl: %s", strerror(errno)); + return NULL; + } + memset(&addr, 0, sizeof(addr)); + /* Bind the socket to CAN interface */ + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + if (bind(socketcan[0].socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + csp_log_error("bind: %s", strerror(errno)); + return NULL; + } + + /* Set filter mode */ + if (promisc == 0) { + + struct can_filter filter; + filter.can_id = CFP_MAKE_DST(csp_get_address()); + filter.can_mask = CFP_MAKE_DST((1 << CFP_HOST_SIZE) - 1); + + if (setsockopt(socketcan[0].socket, SOL_CAN_RAW, CAN_RAW_FILTER, &filter, sizeof(filter)) < 0) { + csp_log_error("setsockopt: %s", strerror(errno)); + return NULL; + } + + } + + /* Create receive thread */ + if (pthread_create(&rx_thread, NULL, socketcan_rx_thread, NULL) != 0) { + csp_log_error("pthread_create: %s", strerror(errno)); + return NULL; + } + + csp_iflist_add(&socketcan[0].interface); + + return &socketcan[0].interface; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_linux.c b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_linux.c new file mode 100644 index 00000000..c4ceeb27 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_linux.c @@ -0,0 +1,254 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int fd; +usart_callback_t usart_callback = NULL; + +static void *serial_rx_thread(void *vptr_args); + +int getbaud(int ifd) { + struct termios termAttr; + int inputSpeed = -1; + speed_t baudRate; + tcgetattr(ifd, &termAttr); + /* Get the input speed. */ + baudRate = cfgetispeed(&termAttr); + switch (baudRate) { + case B0: + inputSpeed = 0; + break; + case B50: + inputSpeed = 50; + break; + case B110: + inputSpeed = 110; + break; + case B134: + inputSpeed = 134; + break; + case B150: + inputSpeed = 150; + break; + case B200: + inputSpeed = 200; + break; + case B300: + inputSpeed = 300; + break; + case B600: + inputSpeed = 600; + break; + case B1200: + inputSpeed = 1200; + break; + case B1800: + inputSpeed = 1800; + break; + case B2400: + inputSpeed = 2400; + break; + case B4800: + inputSpeed = 4800; + break; + case B9600: + inputSpeed = 9600; + break; + case B19200: + inputSpeed = 19200; + break; + case B38400: + inputSpeed = 38400; + break; + case B57600: + inputSpeed = 57600; + break; + case B115200: + inputSpeed = 115200; + break; + case B230400: + inputSpeed = 230400; + break; +#ifndef CSP_MACOSX + case B460800: + inputSpeed = 460800; + break; + case B500000: + inputSpeed = 500000; + break; + case B576000: + inputSpeed = 576000; + break; + case B921600: + inputSpeed = 921600; + break; + case B1000000: + inputSpeed = 1000000; + break; + case B1152000: + inputSpeed = 1152000; + break; + case B1500000: + inputSpeed = 1500000; + break; + case B2000000: + inputSpeed = 2000000; + break; + case B2500000: + inputSpeed = 2500000; + break; + case B3000000: + inputSpeed = 3000000; + break; + case B3500000: + inputSpeed = 3500000; + break; + case B4000000: + inputSpeed = 4000000; + break; +#endif + } + + return inputSpeed; + +} + +void usart_init(struct usart_conf * conf) { + + struct termios options; + pthread_t rx_thread; + + fd = open(conf->device, O_RDWR | O_NOCTTY | O_NONBLOCK); + + if (fd < 0) { + printf("Failed to open %s: %s\r\n", conf->device, strerror(errno)); + return; + } + + int brate = 0; + switch(conf->baudrate) { + case 4800: brate=B4800; break; + case 9600: brate=B9600; break; + case 19200: brate=B19200; break; + case 38400: brate=B38400; break; + case 57600: brate=B57600; break; + case 115200: brate=B115200; break; + case 230400: brate=B230400; break; +#ifndef CSP_MACOSX + case 460800: brate=B460800; break; + case 500000: brate=B500000; break; + case 576000: brate=B576000; break; + case 921600: brate=B921600; break; + case 1000000: brate=B1000000; break; + case 1152000: brate=B1152000; break; + case 1500000: brate=B1500000; break; + case 2000000: brate=B2000000; break; + case 2500000: brate=B2500000; break; + case 3000000: brate=B3000000; break; + case 3500000: brate=B3500000; break; + case 4000000: brate=B4000000; break; +#endif + default: + printf("Unsupported baudrate requested, defaulting to 500000, requested baudrate=%u\n", conf->baudrate); + brate=B500000; + break; + } + + tcgetattr(fd, &options); + cfsetispeed(&options, brate); + cfsetospeed(&options, brate); + options.c_cflag |= (CLOCAL | CREAD); + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + options.c_cflag &= ~CSIZE; + options.c_cflag |= CS8; + options.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); + options.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON); + options.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST); + options.c_cc[VTIME] = 0; + options.c_cc[VMIN] = 1; + tcsetattr(fd, TCSANOW, &options); + if (tcgetattr(fd, &options) == -1) + perror("error setting options"); + fcntl(fd, F_SETFL, 0); + + /* Flush old transmissions */ + if (tcflush(fd, TCIOFLUSH) == -1) + printf("Error flushing serial port - %s(%d).\n", strerror(errno), errno); + + if (pthread_create(&rx_thread, NULL, serial_rx_thread, NULL) != 0) + return; + +} + +void usart_set_callback(usart_callback_t callback) { + usart_callback = callback; +} + +void usart_insert(char c, void * pxTaskWoken) { + printf("%c", c); +} + +void usart_putstr(char * buf, int len) { + if (write(fd, buf, len) != len) + return; +} + +void usart_putc(char c) { + if (write(fd, &c, 1) != 1) + return; +} + +char usart_getc(void) { + char c; + if (read(fd, &c, 1) != 1) return 0; + return c; +} + +static void *serial_rx_thread(void *vptr_args) { + unsigned int length; + uint8_t * cbuf = malloc(100000); + + // Receive loop + while (1) { + length = read(fd, cbuf, 300); + if (length <= 0) { + perror("Error: "); + exit(1); + } + if (usart_callback) + usart_callback(cbuf, length, NULL); + } + return NULL; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_windows.c b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_windows.c new file mode 100644 index 00000000..91ffe87d --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/drivers/usart/usart_windows.c @@ -0,0 +1,230 @@ +#include +#include +#include + +#include +#include + +static HANDLE portHandle = INVALID_HANDLE_VALUE; +static HANDLE rxThread = INVALID_HANDLE_VALUE; +static CRITICAL_SECTION txSection; +static LONG isListening = 0; +static usart_callback_t usart_callback = NULL; + +static void prvSendData(char *buf, int bufsz); +static int prvTryOpenPort(const char* intf); +static int prvTryConfigurePort(const struct usart_conf*); +static int prvTrySetPortTimeouts(void); +static const char* prvParityToStr(BYTE paritySetting); + +#ifdef CSP_DEBUG +static void prvPrintError(void) { + char *messageBuffer = NULL; + DWORD errorCode = GetLastError(); + DWORD formatMessageRet; + formatMessageRet = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + errorCode, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (char*)&messageBuffer, + 0, + NULL); + + if( !formatMessageRet ) { + csp_log_error("FormatMessage error, code: %lu", GetLastError()); + return; + } + csp_log_error("%s", messageBuffer); + LocalFree(messageBuffer); +} +#endif + +#ifdef CSP_DEBUG +#define printError() prvPrintError() +#else +#define printError() do {} while(0) +#endif + +static int prvTryOpenPort(const char *intf) { + portHandle = CreateFileA( + intf, + GENERIC_READ|GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if( portHandle == INVALID_HANDLE_VALUE ) { + DWORD errorCode = GetLastError(); + if( errorCode == ERROR_FILE_NOT_FOUND ) { + csp_log_error("Could not open serial port, because it didn't exist!"); + } + else + csp_log_error("Failure opening serial port! Code: %lu", errorCode); + return 1; + } + return 0; +} + +static int prvTryConfigurePort(const struct usart_conf * conf) { + DCB portSettings = {0}; + portSettings.DCBlength = sizeof(DCB); + if(!GetCommState(portHandle, &portSettings) ) { + csp_log_error("Could not get default settings for open COM port! Code: %lu", GetLastError()); + return -1; + } + portSettings.BaudRate = conf->baudrate; + portSettings.Parity = conf->paritysetting; + portSettings.StopBits = conf->stopbits; + portSettings.fParity = conf->checkparity; + portSettings.fBinary = TRUE; + portSettings.ByteSize = conf->databits; + if( !SetCommState(portHandle, &portSettings) ) { + csp_log_error("Error when setting COM port settings! Code:%lu", GetLastError()); + return 1; + } + + GetCommState(portHandle, &portSettings); + + csp_log_info("Port: %s, Baudrate: %lu, Data bits: %d, Stop bits: %d, Parity: %s", + conf->device, conf->baudrate, conf->databits, conf->stopbits, prvParityToStr(conf->paritysetting)); + return 0; +} + +static const char* prvParityToStr(BYTE paritySetting) { + static const char *parityStr[] = { + "None", + "Odd", + "Even", + "N/A" + }; + char const *resultStr = NULL; + + switch(paritySetting) { + case NOPARITY: + resultStr = parityStr[0]; + break; + case ODDPARITY: + resultStr = parityStr[1]; + break; + case EVENPARITY: + resultStr = parityStr[2]; + break; + default: + resultStr = parityStr[3]; + }; + return resultStr; +} + +static int prvTrySetPortTimeouts(void) { + COMMTIMEOUTS timeouts = {0}; + + if( !GetCommTimeouts(portHandle, &timeouts) ) { + csp_log_error("Error gettings current timeout settings"); + return 1; + } + + timeouts.ReadIntervalTimeout = 5; + timeouts.ReadTotalTimeoutMultiplier = 1; + timeouts.ReadTotalTimeoutConstant = 5; + timeouts.WriteTotalTimeoutMultiplier = 1; + timeouts.WriteTotalTimeoutConstant = 5; + + if(!SetCommTimeouts(portHandle, &timeouts)) { + csp_log_error("Error setting timeouts!"); + return 1; + } + + return 0; +} + +unsigned WINAPI prvRxTask(void* params) { + DWORD bytesRead; + DWORD eventStatus; + uint8_t recvBuffer[24]; + SetCommMask(portHandle, EV_RXCHAR); + + while(isListening) { + WaitCommEvent(portHandle, &eventStatus, NULL); + if( !(eventStatus & EV_RXCHAR) ) { + continue; + } + if( !ReadFile(portHandle, recvBuffer, 24, &bytesRead, NULL)) { + csp_log_warn("Error receiving data! Code: %lu", GetLastError()); + continue; + } + if( usart_callback != NULL ) + usart_callback(recvBuffer, (size_t)bytesRead, NULL); + } + return 0; +} + +static void prvSendData(char *buf, int bufsz) { + DWORD bytesTotal = 0; + DWORD bytesActual; + if( !WriteFile(portHandle, buf, bufsz-bytesTotal, &bytesActual, NULL) ) { + csp_log_error("Could not write data. Code: %lu", GetLastError()); + return; + } + if( !FlushFileBuffers(portHandle) ) { + csp_log_warn("Could not flush write buffer. Code: %lu", GetLastError()); + } +} + +void usart_shutdown(void) { + InterlockedExchange(&isListening, 0); + CloseHandle(portHandle); + portHandle = INVALID_HANDLE_VALUE; + if( rxThread != INVALID_HANDLE_VALUE ) { + WaitForSingleObject(rxThread, INFINITE); + rxThread = INVALID_HANDLE_VALUE; + } + DeleteCriticalSection(&txSection); +} + +void usart_listen(void) { + InterlockedExchange(&isListening, 1); + rxThread = (HANDLE)_beginthreadex(NULL, 0, &prvRxTask, NULL, 0, NULL); +} + +void usart_putstr(char* buf, int bufsz) { + EnterCriticalSection(&txSection); + prvSendData(buf, bufsz); + LeaveCriticalSection(&txSection); +} + +void usart_insert(char c, void *pxTaskWoken) { + /* redirect debug output to stdout */ + printf("%c", c); +} + +void usart_set_callback(usart_callback_t callback) { + usart_callback = callback; +} + +void usart_init(struct usart_conf * conf) { + if( prvTryOpenPort(conf->device) ) { + printError(); + return; + } + + if( prvTryConfigurePort(conf) ) { + printError(); + return; + } + + if( prvTrySetPortTimeouts() ) { + printError(); + return; + } + + InitializeCriticalSection(&txSection); + + /* Start receiver thread */ + usart_listen(); +} + + diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can.c new file mode 100644 index 00000000..5add8334 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can.c @@ -0,0 +1,279 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* CAN frames contains at most 8 bytes of data, so in order to transmit CSP + * packets larger than this, a fragmentation protocol is required. The CAN + * Fragmentation Protocol (CFP) header is designed to match the 29 bit CAN + * identifier. + * + * The CAN identifier is divided in these fields: + * src: 5 bits + * dst: 5 bits + * type: 1 bit + * remain: 8 bits + * identifier: 10 bits + * + * Source and Destination addresses must match 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. Remain indicates number of remaining fragments, and must be + * decremented by one for each fragment sent. The identifier field serves the + * same purpose as in the Internet Protocol, and should be an auto incrementing + * integer to uniquely separate sessions. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "csp_if_can_pbuf.h" + +/* CFP Frame Types */ +enum cfp_frame_t { + CFP_BEGIN = 0, + CFP_MORE = 1 +}; + +int csp_can_rx(csp_iface_t *interface, uint32_t id, const uint8_t *data, uint8_t dlc, CSP_BASE_TYPE *task_woken) +{ + csp_can_pbuf_element_t *buf; + uint8_t offset; + + /* Random packet loss */ +#if 0 + int random = rand(); + if (random < RAND_MAX * 0.00005) { + csp_log_warn("Dropping frame"); + return; + } +#endif + + /* Bind incoming frame to a packet buffer */ + buf = csp_can_pbuf_find(id, CFP_ID_CONN_MASK); + + /* Check returned buffer */ + if (buf == NULL) { + if (CFP_TYPE(id) == CFP_BEGIN) { + buf = csp_can_pbuf_new(id, task_woken); + if (buf == NULL) { + //csp_log_warn("No available packet buffer for CAN"); + interface->rx_error++; + return CSP_ERR_NOMEM; + } + } else { + //csp_log_warn("Out of order id 0x%X remain %u", CFP_ID(id), CFP_REMAIN(id)); + interface->frame++; + return CSP_ERR_INVAL; + } + } + + /* Reset frame data offset */ + offset = 0; + + switch (CFP_TYPE(id)) { + + case CFP_BEGIN: + + /* Discard packet if DLC is less than CSP id + CSP length fields */ + if (dlc < sizeof(csp_id_t) + sizeof(uint16_t)) { + //csp_log_warn("Short BEGIN frame received"); + interface->frame++; + csp_can_pbuf_free(buf, task_woken); + break; + } + + /* Check for incomplete frame */ + if (buf->packet != NULL) { + /* Reuse the buffer */ + //csp_log_warn("Incomplete frame"); + interface->frame++; + } else { + /* Allocate memory for frame */ + if (task_woken == NULL) { + buf->packet = csp_buffer_get(interface->mtu); + } else { + buf->packet = csp_buffer_get_isr(interface->mtu); + } + if (buf->packet == NULL) { + //csp_log_error("Failed to get buffer for CSP_BEGIN packet"); + interface->frame++; + csp_can_pbuf_free(buf, task_woken); + break; + } + } + + /* Copy CSP identifier and length*/ + memcpy(&(buf->packet->id), data, sizeof(csp_id_t)); + buf->packet->id.ext = csp_ntoh32(buf->packet->id.ext); + memcpy(&(buf->packet->length), data + sizeof(csp_id_t), sizeof(uint16_t)); + buf->packet->length = csp_ntoh16(buf->packet->length); + + /* Reset RX count */ + buf->rx_count = 0; + + /* Set offset to prevent CSP header from being copied to CSP data */ + offset = sizeof(csp_id_t) + sizeof(uint16_t); + + /* Set remain field - increment to include begin packet */ + buf->remain = CFP_REMAIN(id) + 1; + + /* FALLTHROUGH */ + + case CFP_MORE: + + /* Check 'remain' field match */ + if (CFP_REMAIN(id) != buf->remain - 1) { + //csp_log_error("CAN frame lost in CSP packet"); + csp_can_pbuf_free(buf, task_woken); + interface->frame++; + break; + } + + /* Decrement remaining frames */ + buf->remain--; + + /* Check for overflow */ + if ((buf->rx_count + dlc - offset) > buf->packet->length) { + //csp_log_error("RX buffer overflow"); + interface->frame++; + csp_can_pbuf_free(buf, task_woken); + break; + } + + /* Copy dlc bytes into buffer */ + memcpy(&buf->packet->data[buf->rx_count], data + offset, dlc - offset); + buf->rx_count += dlc - offset; + + /* Check if more data is expected */ + if (buf->rx_count != buf->packet->length) + break; + + /* Data is available */ + csp_qfifo_write(buf->packet, interface, task_woken); + + /* Drop packet buffer reference */ + buf->packet = NULL; + + /* Free packet buffer */ + csp_can_pbuf_free(buf, task_woken); + + break; + + default: + //csp_log_warn("Received unknown CFP message type"); + csp_can_pbuf_free(buf, task_woken); + break; + + } + + return CSP_ERR_NONE; +} + +int csp_can_tx(csp_iface_t *interface, csp_packet_t *packet, uint32_t timeout) +{ + + /* CFP Identification number */ + static volatile int csp_can_frame_id = 0; + + /* Get local copy of the static frameid */ + int ident = csp_can_frame_id++; + + uint16_t tx_count; + uint8_t bytes, overhead, avail, dest; + uint8_t frame_buf[8]; + + /* Calculate overhead */ + overhead = sizeof(csp_id_t) + sizeof(uint16_t); + + /* Insert destination node mac address into the CFP destination field */ + dest = csp_rtable_find_mac(packet->id.dst); + if (dest == CSP_NODE_MAC) + dest = packet->id.dst; + + /* Create CAN identifier */ + uint32_t id = 0; + id |= CFP_MAKE_SRC(packet->id.src); + id |= CFP_MAKE_DST(dest); + id |= CFP_MAKE_ID(ident); + id |= CFP_MAKE_TYPE(CFP_BEGIN); + id |= CFP_MAKE_REMAIN((packet->length + overhead - 1) / 8); + + /* Calculate first frame data bytes */ + avail = 8 - overhead; + bytes = (packet->length <= avail) ? packet->length : avail; + + /* Copy CSP headers and data */ + uint32_t csp_id_be = csp_hton32(packet->id.ext); + uint16_t csp_length_be = csp_hton16(packet->length); + + memcpy(frame_buf, &csp_id_be, sizeof(csp_id_be)); + memcpy(frame_buf + sizeof(csp_id_be), &csp_length_be, sizeof(csp_length_be)); + memcpy(frame_buf + overhead, packet->data, bytes); + + /* Increment tx counter */ + tx_count = bytes; + + /* Send first frame */ + if (csp_can_tx_frame(interface, id, frame_buf, overhead + bytes)) { + //csp_log_warn("Failed to send CAN frame in csp_tx_can"); + interface->tx_error++; + return CSP_ERR_DRIVER; + } + + /* Send next frames if not complete */ + while (tx_count < packet->length) { + /* Calculate frame data bytes */ + bytes = (packet->length - tx_count >= 8) ? 8 : packet->length - tx_count; + + /* Prepare identifier */ + id = 0; + id |= CFP_MAKE_SRC(packet->id.src); + id |= CFP_MAKE_DST(dest); + id |= CFP_MAKE_ID(ident); + id |= CFP_MAKE_TYPE(CFP_MORE); + id |= CFP_MAKE_REMAIN((packet->length - tx_count - bytes + 7) / 8); + + /* Increment tx counter */ + tx_count += bytes; + + /* Send frame */ + if (csp_can_tx_frame(interface, id, packet->data + tx_count - bytes, bytes)) { + //csp_log_warn("Failed to send CAN frame in Tx callback"); + interface->tx_error++; + return CSP_ERR_DRIVER; + } + } + + csp_buffer_free(packet); + + return CSP_ERR_NONE; +} diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.c new file mode 100644 index 00000000..65f18de9 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.c @@ -0,0 +1,77 @@ +/* + * csp_if_can_pbuf.c + * + * Created on: Feb 3, 2017 + * Author: johan + */ + +#include +#include "csp_if_can_pbuf.h" + +/* Number of packet buffer elements */ +#define PBUF_ELEMENTS CSP_CONN_MAX + +/* Buffer element timeout in ms */ +#define PBUF_TIMEOUT_MS 1000 + +static csp_can_pbuf_element_t csp_can_pbuf[PBUF_ELEMENTS] = {}; + +int csp_can_pbuf_free(csp_can_pbuf_element_t *buf, CSP_BASE_TYPE *task_woken) +{ + /* Free CSP packet */ + if (buf->packet != NULL) { + if (task_woken == NULL) { + csp_buffer_free(buf->packet); + } else { + csp_buffer_free_isr(buf->packet); + } + } + + /* Mark buffer element free */ + buf->packet = NULL; + buf->rx_count = 0; + buf->cfpid = 0; + buf->last_used = 0; + buf->remain = 0; + buf->state = BUF_FREE; + + return CSP_ERR_NONE; +} + +csp_can_pbuf_element_t *csp_can_pbuf_new(uint32_t id, CSP_BASE_TYPE *task_woken) +{ + uint32_t now = csp_get_ms(); + + for (int i = 0; i < PBUF_ELEMENTS; i++) { + + /* Perform cleanup in used pbufs */ + if (csp_can_pbuf[i].state == BUF_USED) { + if (now - csp_can_pbuf[i].last_used > PBUF_TIMEOUT_MS) + csp_can_pbuf_free(&csp_can_pbuf[i], task_woken); + } + + if (csp_can_pbuf[i].state == BUF_FREE) { + csp_can_pbuf[i].state = BUF_USED; + csp_can_pbuf[i].cfpid = id; + csp_can_pbuf[i].remain = 0; + csp_can_pbuf[i].last_used = now; + return &csp_can_pbuf[i]; + } + + } + + return NULL; + +} + +csp_can_pbuf_element_t *csp_can_pbuf_find(uint32_t id, uint32_t mask) +{ + for (int i = 0; i < PBUF_ELEMENTS; i++) { + if ((csp_can_pbuf[i].state == BUF_USED) && ((csp_can_pbuf[i].cfpid & mask) == (id & mask))) { + csp_can_pbuf[i].last_used = csp_get_ms(); + return &csp_can_pbuf[i]; + } + } + return NULL; +} + diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.h b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.h new file mode 100644 index 00000000..3e71c26c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_can_pbuf.h @@ -0,0 +1,31 @@ +/* + * csp_if_can_pbuf.h + * + * Created on: Feb 3, 2017 + * Author: johan + */ + +#ifndef LIB_CSP_SRC_INTERFACES_CSP_IF_CAN_PBUF_H_ +#define LIB_CSP_SRC_INTERFACES_CSP_IF_CAN_PBUF_H_ + +/* Packet buffers */ +typedef enum { + BUF_FREE = 0, /* Buffer element free */ + BUF_USED = 1, /* Buffer element used */ +} csp_can_pbuf_state_t; + +typedef struct { + uint16_t rx_count; /* Received bytes */ + uint32_t remain; /* Remaining packets */ + uint32_t cfpid; /* Connection CFP identification number */ + csp_packet_t *packet; /* Pointer to packet buffer */ + csp_can_pbuf_state_t state; /* Element state */ + uint32_t last_used; /* Timestamp in ms for last use of buffer */ +} csp_can_pbuf_element_t; + +int csp_can_pbuf_free(csp_can_pbuf_element_t *buf, CSP_BASE_TYPE *task_woken); +csp_can_pbuf_element_t *csp_can_pbuf_new(uint32_t id, CSP_BASE_TYPE *task_woken); +csp_can_pbuf_element_t *csp_can_pbuf_find(uint32_t id, uint32_t mask); +void csp_can_pbuf_cleanup(CSP_BASE_TYPE *task_woken); + +#endif /* LIB_CSP_SRC_INTERFACES_CSP_IF_CAN_PBUF_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_i2c.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_i2c.c new file mode 100644 index 00000000..c5d105df --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_i2c.c @@ -0,0 +1,116 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +static int csp_i2c_handle = 0; + +int csp_i2c_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + /* Cast the CSP packet buffer into an i2c frame */ + i2c_frame_t * frame = (i2c_frame_t *) packet; + + /* Insert destination node into the i2c destination field */ + if (csp_rtable_find_mac(packet->id.dst) == CSP_NODE_MAC) { + frame->dest = packet->id.dst; + } else { + frame->dest = csp_rtable_find_mac(packet->id.dst); + } + + /* Save the outgoing id in the buffer */ + packet->id.ext = csp_hton32(packet->id.ext); + + /* Add the CSP header to the I2C length field */ + frame->len += sizeof(packet->id); + frame->len_rx = 0; + + /* Some I2C drivers support X number of retries + * CSP don't care about this. If it doesn't work the first + * time, don'y use time on it. + */ + frame->retries = 0; + + /* enqueue the frame */ + if (i2c_send(csp_i2c_handle, frame, timeout) != E_NO_ERR) + return CSP_ERR_DRIVER; + + return CSP_ERR_NONE; + +} + +/** + * When a frame is received, cast it to a csp_packet + * and send it directly to the CSP new packet function. + * Context: ISR only + * @param frame + */ +void csp_i2c_rx(i2c_frame_t * frame, void * pxTaskWoken) { + + static csp_packet_t * packet; + + /* Validate input */ + if (frame == NULL) + return; + + if ((frame->len < 4) || (frame->len > I2C_MTU)) { + csp_if_i2c.frame++; + csp_buffer_free_isr(frame); + return; + } + + /* Strip the CSP header off the length field before converting to CSP packet */ + frame->len -= sizeof(csp_id_t); + + /* Convert the packet from network to host order */ + packet = (csp_packet_t *) frame; + packet->id.ext = csp_ntoh32(packet->id.ext); + + /* Receive the packet in CSP */ + csp_qfifo_write(packet, &csp_if_i2c, pxTaskWoken); + +} + +int csp_i2c_init(uint8_t addr, int handle, int speed) { + + /* Create i2c_handle */ + csp_i2c_handle = handle; + if (i2c_init(csp_i2c_handle, I2C_MASTER, addr, speed, 10, 10, csp_i2c_rx) != E_NO_ERR) + return CSP_ERR_DRIVER; + + /* Register interface */ + csp_iflist_add(&csp_if_i2c); + + return CSP_ERR_NONE; + +} + +/** Interface definition */ +csp_iface_t csp_if_i2c = { + .name = "I2C", + .nexthop = csp_i2c_tx, +}; diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_kiss.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_kiss.c new file mode 100644 index 00000000..fe5707f6 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_kiss.c @@ -0,0 +1,260 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define KISS_MTU 256 + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + +#define TNC_DATA 0x00 +#define TNC_SET_HARDWARE 0x06 +#define TNC_RETURN 0xFF + +static int kiss_lock_init = 0; +static csp_bin_sem_handle_t kiss_lock; + +/* Send a CSP packet over the KISS RS232 protocol */ +static int csp_kiss_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + if (interface == NULL || interface->driver == NULL) + return CSP_ERR_DRIVER; + + /* Add CRC32 checksum */ + csp_crc32_append(packet, false); + + /* Save the outgoing id in the buffer */ + packet->id.ext = csp_hton32(packet->id.ext); + packet->length += sizeof(packet->id.ext); + + /* Lock */ + csp_bin_sem_wait(&kiss_lock, 1000); + + /* Transmit data */ + csp_kiss_handle_t * driver = interface->driver; + driver->kiss_putc(FEND); + driver->kiss_putc(TNC_DATA); + for (unsigned int i = 0; i < packet->length; i++) { + if (((unsigned char *) &packet->id.ext)[i] == FEND) { + ((unsigned char *) &packet->id.ext)[i] = TFEND; + driver->kiss_putc(FESC); + } else if (((unsigned char *) &packet->id.ext)[i] == FESC) { + ((unsigned char *) &packet->id.ext)[i] = TFESC; + driver->kiss_putc(FESC); + } + driver->kiss_putc(((unsigned char *) &packet->id.ext)[i]); + } + driver->kiss_putc(FEND); + + /* Free data */ + csp_buffer_free(packet); + + /* Unlock */ + csp_bin_sem_post(&kiss_lock); + + return CSP_ERR_NONE; +} + +/** + * When a frame is received, decode the kiss-stuff + * and eventually send it directly to the CSP new packet function. + */ +void csp_kiss_rx(csp_iface_t * interface, uint8_t * buf, int len, void * pxTaskWoken) { + + /* Driver handle */ + csp_kiss_handle_t * driver = interface->driver; + + while (len--) { + + /* Input */ + unsigned char inputbyte = *buf++; + + /* If packet was too long */ + if (driver->rx_length > interface->mtu) { + //csp_log_warn("KISS RX overflow"); + interface->rx_error++; + driver->rx_mode = KISS_MODE_NOT_STARTED; + driver->rx_length = 0; + } + + switch (driver->rx_mode) { + + case KISS_MODE_NOT_STARTED: + + /* Send normal chars back to usart driver */ + if (inputbyte != FEND) { + if (driver->kiss_discard != NULL) + driver->kiss_discard(inputbyte, pxTaskWoken); + break; + } + + /* Try to allocate new buffer */ + if (driver->rx_packet == NULL) { + if (pxTaskWoken == NULL) { + driver->rx_packet = csp_buffer_get(interface->mtu); + } else { + driver->rx_packet = csp_buffer_get_isr(interface->mtu); + } + } + + /* If no more memory, skip frame */ + if (driver->rx_packet == NULL) { + driver->rx_mode = KISS_MODE_SKIP_FRAME; + break; + } + + /* Start transfer */ + driver->rx_length = 0; + driver->rx_mode = KISS_MODE_STARTED; + driver->rx_first = 1; + break; + + case KISS_MODE_STARTED: + + /* Escape char */ + if (inputbyte == FESC) { + driver->rx_mode = KISS_MODE_ESCAPED; + break; + } + + /* End Char */ + if (inputbyte == FEND) { + + /* Accept message */ + if (driver->rx_length > 0) { + + /* Check for valid length */ + if (driver->rx_length < CSP_HEADER_LENGTH + sizeof(uint32_t)) { + //csp_log_warn("KISS short frame skipped, len: %u", driver->rx_length); + interface->rx_error++; + driver->rx_mode = KISS_MODE_NOT_STARTED; + break; + } + + /* Count received frame */ + interface->frame++; + + /* The CSP packet length is without the header */ + driver->rx_packet->length = driver->rx_length - CSP_HEADER_LENGTH; + + /* Convert the packet from network to host order */ + driver->rx_packet->id.ext = csp_ntoh32(driver->rx_packet->id.ext); + + /* Validate CRC */ + if (csp_crc32_verify(driver->rx_packet, false) != CSP_ERR_NONE) { + //csp_log_warn("KISS invalid crc frame skipped, len: %u", driver->rx_packet->length); + interface->rx_error++; + driver->rx_mode = KISS_MODE_NOT_STARTED; + break; + } + + /* Send back into CSP, notice calling from task so last argument must be NULL! */ + csp_qfifo_write(driver->rx_packet, interface, pxTaskWoken); + driver->rx_packet = NULL; + driver->rx_mode = KISS_MODE_NOT_STARTED; + break; + + } + + /* Break after the end char */ + break; + } + + /* Skip the first char after FEND which is TNC_DATA (0x00) */ + if (driver->rx_first) { + driver->rx_first = 0; + break; + } + + /* Valid data char */ + ((char *) &driver->rx_packet->id.ext)[driver->rx_length++] = inputbyte; + + break; + + case KISS_MODE_ESCAPED: + + /* Escaped escape char */ + if (inputbyte == TFESC) + ((char *) &driver->rx_packet->id.ext)[driver->rx_length++] = FESC; + + /* Escaped fend char */ + if (inputbyte == TFEND) + ((char *) &driver->rx_packet->id.ext)[driver->rx_length++] = FEND; + + /* Go back to started mode */ + driver->rx_mode = KISS_MODE_STARTED; + break; + + case KISS_MODE_SKIP_FRAME: + + /* Just wait for end char */ + if (inputbyte == FEND) + driver->rx_mode = KISS_MODE_NOT_STARTED; + + break; + + } + + } + +} + +void csp_kiss_init(csp_iface_t * csp_iface, csp_kiss_handle_t * csp_kiss_handle, csp_kiss_putc_f kiss_putc_f, csp_kiss_discard_f kiss_discard_f, const char * name) { + + /* Init lock only once */ + if (kiss_lock_init == 0) { + csp_bin_sem_create(&kiss_lock); + kiss_lock_init = 1; + } + + /* Register device handle as member of interface */ + csp_iface->driver = csp_kiss_handle; + csp_kiss_handle->kiss_discard = kiss_discard_f; + csp_kiss_handle->kiss_putc = kiss_putc_f; + csp_kiss_handle->rx_packet = NULL; + csp_kiss_handle->rx_mode = KISS_MODE_NOT_STARTED; + + /* Set default MTU if not given */ + if (csp_iface->mtu == 0) { + csp_iface->mtu = KISS_MTU; + } + + /* Setup other mandatories */ + csp_iface->nexthop = csp_kiss_tx; + csp_iface->name = name; + + /* Regsiter interface */ + csp_iflist_add(csp_iface); + +} diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_lo.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_lo.c new file mode 100644 index 00000000..f3e81b15 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_lo.c @@ -0,0 +1,61 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* CSP includes */ +#include +#include +#include +#include + +#include +#include + +#include "../csp_route.h" + +/** + * Loopback interface transmit function + * @param packet Packet to transmit + * @param timeout Timout in ms + * @return 1 if packet was successfully transmitted, 0 on error + */ +static int csp_lo_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + /* Drop packet silently if not destined for us. This allows + * blackhole routing addresses by setting their nexthop to + * the loopback interface. + */ + if (packet->id.dst != csp_get_address()) { + /* Consume and drop packet */ + csp_buffer_free(packet); + return CSP_ERR_NONE; + } + + /* Send back into CSP, notice calling from task so last argument must be NULL! */ + csp_qfifo_write(packet, &csp_if_lo, NULL); + + return CSP_ERR_NONE; + +} + +/* Interface definition */ +csp_iface_t csp_if_lo = { + .name = "LOOP", + .nexthop = csp_lo_tx, +}; diff --git a/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_zmqhub.c b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_zmqhub.c new file mode 100644 index 00000000..5292663b --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/interfaces/csp_if_zmqhub.c @@ -0,0 +1,165 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +/* CSP includes */ +#include +#include +#include +#include +#include +#include + +/* ZMQ */ +#include + +static void * context; +static void * publisher; +static void * subscriber; +static csp_bin_sem_handle_t tx_wait; + +/** + * Interface transmit function + * @param packet Packet to transmit + * @param timeout Timeout in ms + * @return 1 if packet was successfully transmitted, 0 on error + */ +int csp_zmqhub_tx(csp_iface_t * interface, csp_packet_t * packet, uint32_t timeout) { + + /* Send envelope */ + uint8_t dest = csp_rtable_find_mac(packet->id.dst); + if (dest == CSP_NODE_MAC) + dest = packet->id.dst; + + uint16_t length = packet->length; + uint8_t * destptr = ((uint8_t *) &packet->id) - sizeof(dest); + memcpy(destptr, &dest, sizeof(dest)); + csp_bin_sem_wait(&tx_wait, CSP_INFINITY); /* Using ZMQ in thread safe manner*/ + int result = zmq_send(publisher, destptr, length + sizeof(packet->id) + sizeof(dest), 0); + csp_bin_sem_post(&tx_wait); /* Release tx semaphore */ + if (result < 0) + csp_log_error("ZMQ send error: %u %s\r\n", result, strerror(result)); + + csp_buffer_free(packet); + + return CSP_ERR_NONE; + +} + +CSP_DEFINE_TASK(csp_zmqhub_task) { + + while(1) { + zmq_msg_t msg; + assert(zmq_msg_init_size(&msg, 1024) == 0); + + /* Receive data */ + if (zmq_msg_recv(&msg, subscriber, 0) < 0) { + zmq_msg_close(&msg); + csp_log_error("ZMQ: %s", zmq_strerror(zmq_errno())); + continue; + } + + int datalen = zmq_msg_size(&msg); + if (datalen < 5) { + csp_log_warn("ZMQ: Too short datalen: %u", datalen); + while(zmq_msg_recv(&msg, subscriber, ZMQ_NOBLOCK) > 0) + zmq_msg_close(&msg); + continue; + } + + /* Create new csp packet */ + csp_packet_t * packet = csp_buffer_get(256); + if (packet == NULL) { + zmq_msg_close(&msg); + continue; + } + + /* Copy the data from zmq to csp */ + uint8_t * destptr = ((uint8_t *) &packet->id) - sizeof(*destptr); + memcpy(destptr, zmq_msg_data(&msg), datalen); + packet->length = datalen - sizeof(packet->id) - sizeof(*destptr); + + /* Queue up packet to router */ + csp_qfifo_write(packet, &csp_if_zmqhub, NULL); + + zmq_msg_close(&msg); + } + + return CSP_TASK_RETURN; + +} + +int csp_zmqhub_init(uint8_t addr, const char * host) { + char url_pub[100]; + char url_sub[100]; + + sprintf(url_pub, "tcp://%s:6000", host); + sprintf(url_sub, "tcp://%s:7000", host); + + return csp_zmqhub_init_w_endpoints(addr, url_pub, url_sub); +} + +int csp_zmqhub_init_w_endpoints(uint8_t addr, const char * publisher_endpoint, + const char * subscriber_endpoint) { + + context = zmq_ctx_new(); + assert(context); + + csp_log_info("INIT ZMQ with addr %u to servers %s / %s\r\n", + addr, publisher_endpoint, subscriber_endpoint); + + /* Publisher (TX) */ + publisher = zmq_socket(context, ZMQ_PUB); + assert(publisher); + assert(zmq_connect(publisher, publisher_endpoint) == 0); + + /* Subscriber (RX) */ + subscriber = zmq_socket(context, ZMQ_SUB); + assert(subscriber); + assert(zmq_connect(subscriber, subscriber_endpoint) == 0); + + if (addr == CSP_NODE_MAC) { // == 255 + assert(zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, NULL, 0) == 0); + } else { + assert(zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, &addr, 1) == 0); + } + /* ZMQ isn't thread safe, so we add a binary semaphore to wait on for tx */ + if (csp_bin_sem_create(&tx_wait) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to initialize semaphore in csp_zmqhub_init_w_endpoints"); + return CSP_ERR_NOMEM; + } + /* Start RX thread */ + static csp_thread_handle_t handle_subscriber; + int ret = csp_thread_create(csp_zmqhub_task, "ZMQ", 20000, NULL, 0, &handle_subscriber); + csp_log_info("Task start %d\r\n", ret); + + /* Register interface */ + csp_iflist_add(&csp_if_zmqhub); + + return CSP_ERR_NONE; + +} + +/* Interface definition */ +csp_iface_t csp_if_zmqhub = { + .name = "ZMQHUB", + .nexthop = csp_zmqhub_tx, +}; diff --git a/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_cidr.c b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_cidr.c new file mode 100644 index 00000000..5758dc3c --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_cidr.c @@ -0,0 +1,233 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include +#include +#include + +/* Local typedef for routing table */ +typedef struct __attribute__((__packed__)) csp_rtable_s { + uint8_t address; + uint8_t netmask; + uint8_t mac; + csp_iface_t * interface; + struct csp_rtable_s * next; +} csp_rtable_t; + +/* Routing entries are stored in a linked list*/ +static csp_rtable_t * rtable = NULL; + +static csp_rtable_t * csp_rtable_find(uint8_t addr, uint8_t netmask, uint8_t exact) { + + /* Remember best result */ + csp_rtable_t * best_result = NULL; + uint8_t best_result_mask = 0; + + /* Start search */ + csp_rtable_t * i = rtable; + while(i) { + + /* Look for exact match */ + if (i->address == addr && i->netmask == netmask) { + best_result = i; + break; + } + + /* Try a CIDR netmask match */ + if (!exact) { + uint8_t hostbits = (1 << (CSP_ID_HOST_SIZE - i->netmask)) - 1; + uint8_t netbits = ~hostbits; + //printf("Netbits %x Hostbits %x\r\n", netbits, hostbits); + + /* Match network addresses */ + uint8_t net_a = i->address & netbits; + uint8_t net_b = addr & netbits; + //printf("A: %hhx, B: %hhx\r\n", net_a, net_b); + + /* We have a match */ + if (net_a == net_b) { + if (i->netmask >= best_result_mask) { + //printf("Match best result %u %u\r\n", best_result_mask, i->netmask); + best_result = i; + best_result_mask = i->netmask; + } + } + + } + + i = i->next; + + } + +#if 0 + if (best_result) + csp_debug(CSP_PACKET, "Using routing entry: %u/%u dev %s m:%u\r\n", best_result->address, best_result->netmask, best_result->interface->name, best_result->mac); +#endif + + return best_result; + +} + +void csp_rtable_clear(void) { + for (csp_rtable_t * i = rtable; (i);) { + void * freeme = i; + i = i->next; + csp_free(freeme); + } + rtable = NULL; + + /* Set loopback up again */ + csp_rtable_set(csp_get_address(), CSP_ID_HOST_SIZE, &csp_if_lo, CSP_NODE_MAC); + +} + +static int csp_rtable_parse(const char * buffer, int dry_run) { + + int valid_entries = 0; + + /* Copy string before running strtok */ + char * str = alloca(strlen(buffer) + 1); + memcpy(str, buffer, strlen(buffer) + 1); + + /* Get first token */ + str = strtok(str, ","); + + while ((str) && (strlen(str) > 1)) { + int address = 0, netmask = 0, mac = 255; + char name[10] = {}; + if (sscanf(str, "%u/%u %s %u", &address, &netmask, name, &mac) != 4) { + if (sscanf(str, "%u/%u %s", &address, &netmask, name) != 3) { + csp_log_error("Parse error %s", str); + return -1; + } + } + //printf("Parsed %u/%u %u %s\r\n", address, netmask, mac, name); + csp_iface_t * ifc = csp_iflist_get_by_name(name); + if (ifc) { + if (dry_run == 0) + csp_rtable_set(address, netmask, ifc, mac); + } else { + csp_log_error("Unknown interface %s", name); + return -1; + } + valid_entries++; + str = strtok(NULL, ","); + } + + return valid_entries; +} + +void csp_rtable_load(const char * buffer) { + csp_rtable_parse(buffer, 0); +} + +int csp_rtable_check(const char * buffer) { + return csp_rtable_parse(buffer, 1); +} + +int csp_rtable_save(char * buffer, int maxlen) { + int len = 0; + for (csp_rtable_t * i = rtable; (i); i = i->next) { + if (i->mac != CSP_NODE_MAC) { + len += snprintf(buffer + len, maxlen - len, "%u/%u %s %u, ", i->address, i->netmask, i->interface->name, i->mac); + } else { + len += snprintf(buffer + len, maxlen - len, "%u/%u %s, ", i->address, i->netmask, i->interface->name); + } + } + return len; +} + +csp_iface_t * csp_rtable_find_iface(uint8_t id) { + csp_rtable_t * entry = csp_rtable_find(id, CSP_ID_HOST_SIZE, 0); + if (entry == NULL) + return NULL; + return entry->interface; +} + +uint8_t csp_rtable_find_mac(uint8_t id) { + csp_rtable_t * entry = csp_rtable_find(id, CSP_ID_HOST_SIZE, 0); + if (entry == NULL) + return 255; + return entry->mac; +} + +int csp_rtable_set(uint8_t _address, uint8_t _netmask, csp_iface_t *ifc, uint8_t mac) { + + if (ifc == NULL) + return CSP_ERR_INVAL; + + /* Set default route in the old way */ + int address, netmask; + if (_address == CSP_DEFAULT_ROUTE) { + netmask = 0; + address = 0; + } else { + netmask = _netmask; + address = _address; + } + + /* Fist see if the entry exists */ + csp_rtable_t * entry = csp_rtable_find(address, netmask, 1); + + /* If not, create a new one */ + if (!entry) { + entry = csp_malloc(sizeof(csp_rtable_t)); + if (entry == NULL) + return CSP_ERR_NOMEM; + + entry->next = NULL; + /* Add entry to linked-list */ + if (rtable == NULL) { + /* This is the first interface to be added */ + rtable = entry; + } else { + /* One or more interfaces were already added */ + csp_rtable_t * i = rtable; + while (i->next) + i = i->next; + i->next = entry; + } + } + + /* Fill in the data */ + entry->address = address; + entry->netmask = netmask; + entry->interface = ifc; + entry->mac = mac; + + return CSP_ERR_NONE; +} + +#ifdef CSP_DEBUG +void csp_rtable_print(void) { + + for (csp_rtable_t * i = rtable; (i); i = i->next) { + if (i->mac == 255) { + printf("%u/%u %s\r\n", i->address, i->netmask, i->interface->name); + } else { + printf("%u/%u %s %u\r\n", i->address, i->netmask, i->interface->name, i->mac); + } + } + +} +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_static.c b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_static.c new file mode 100644 index 00000000..ea993027 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/rtable/csp_rtable_static.c @@ -0,0 +1,128 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +/* Local typedef for routing table */ +typedef struct __attribute__((__packed__)) csp_rtable_s { + csp_iface_t * interface; + uint8_t mac; +} csp_rtable_t; + +/* Static storage context for routing table */ +static csp_rtable_t routes[CSP_ROUTE_COUNT] = {}; + +/** + * Find entry in static routing table + * This is done by table lookup with fallback to the default route + * The reason why the csp_rtable_t struct is not returned directly + * is that we wish to hide the storage format, mainly because of + * the alternative routing table storage (cidr). + * @param id Node + * @return pointer to found routing entry + */ +static csp_rtable_t * csp_rtable_find(uint8_t id) { + + if (routes[id].interface != NULL) { + return &routes[id]; + } else if (routes[CSP_DEFAULT_ROUTE].interface != NULL) { + return &routes[CSP_DEFAULT_ROUTE]; + } + return NULL; + +} + +csp_iface_t * csp_rtable_find_iface(uint8_t id) { + csp_rtable_t * route = csp_rtable_find(id); + if (route == NULL) + return NULL; + return route->interface; +} + +uint8_t csp_rtable_find_mac(uint8_t id) { + csp_rtable_t * route = csp_rtable_find(id); + if (route == NULL) + return 255; + return route->mac; +} + +void csp_rtable_clear(void) { + memset(routes, 0, sizeof(routes[0]) * CSP_ROUTE_COUNT); +} + +void csp_route_table_load(uint8_t route_table_in[CSP_ROUTE_TABLE_SIZE]) { + memcpy(routes, route_table_in, sizeof(routes[0]) * CSP_ROUTE_COUNT); +} + +void csp_route_table_save(uint8_t route_table_out[CSP_ROUTE_TABLE_SIZE]) { + memcpy(route_table_out, routes, sizeof(routes[0]) * CSP_ROUTE_COUNT); +} + +int csp_rtable_set(uint8_t node, uint8_t mask, csp_iface_t *ifc, uint8_t mac) { + + /* Don't add nothing */ + if (ifc == NULL) + return CSP_ERR_INVAL; + + /** + * Check if the interface has been added. + * + * NOTE: For future implementations, interfaces should call + * csp_route_add_if in its csp_if__init function, instead + * of registering at first route_set, in order to make the interface + * available to network based (CMP) route configuration. + */ + csp_iflist_add(ifc); + + /* Set route */ + if (node <= CSP_DEFAULT_ROUTE) { + routes[node].interface = ifc; + routes[node].mac = mac; + } else { + csp_log_error("Failed to set route: invalid node id %u", node); + return CSP_ERR_INVAL; + } + + return CSP_ERR_NONE; + +} + +void csp_rtable_load(const char * buffer) { +} + +int csp_rtable_check(const char * buffer) { + return -1; +} + +#ifdef CSP_DEBUG +void csp_rtable_print(void) { + int i; + printf("Node Interface Address\r\n"); + for (i = 0; i < CSP_DEFAULT_ROUTE; i++) + if (routes[i].interface != NULL) + printf("%4u %-9s %u\r\n", i, + routes[i].interface->name, + routes[i].mac == CSP_NODE_MAC ? i : routes[i].mac); + printf(" * %-9s %u\r\n", routes[CSP_DEFAULT_ROUTE].interface->name, routes[CSP_DEFAULT_ROUTE].mac); + +} +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/transport/csp_rdp.c b/gomspace/libgscsp/lib/libcsp/src/transport/csp_rdp.c new file mode 100644 index 00000000..e19968e2 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/transport/csp_rdp.c @@ -0,0 +1,1102 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + * This is a implementation of the seq/ack handling taken from the Reliable Datagram Protocol (RDP) + * For more information read RFC 908/1151. The implementation has been extended to include support for + * delayed acknowledgments, to improve performance over half-duplex links. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "../csp_port.h" +#include "../csp_conn.h" +#include "../csp_io.h" +#include "csp_transport.h" + +#ifdef CSP_USE_RDP + +#define RDP_SYN 0x01 +#define RDP_ACK 0x02 +#define RDP_EAK 0x04 +#define RDP_RST 0x08 + +static uint32_t csp_rdp_window_size = 4; +static uint32_t csp_rdp_conn_timeout = 10000; +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; + +/* Used for queue calls */ +static CSP_BASE_TYPE pdTrue = 1; + +typedef struct __attribute__((__packed__)) { + /* The timestamp is placed in the padding bytes */ + uint8_t padding[CSP_PADDING_BYTES - 2 * sizeof(uint32_t)]; + uint32_t quarantine; // EACK quarantine period + uint32_t timestamp; // Time the message was sent + uint16_t length; // Length field must be just before CSP ID + csp_id_t id; // CSP id must be just before data + uint8_t data[]; // This just points to the rest of the buffer, without a size indication. +} rdp_packet_t; + +typedef struct __attribute__((__packed__)) { + union __attribute__((__packed__)) { + uint8_t flags; + struct __attribute__((__packed__)) { +#if defined(CSP_BIG_ENDIAN) && !defined(CSP_LITTLE_ENDIAN) + unsigned int res : 4; + unsigned int syn : 1; + unsigned int ack : 1; + unsigned int eak : 1; + unsigned int rst : 1; +#elif defined(CSP_LITTLE_ENDIAN) && !defined(CSP_BIG_ENDIAN) + unsigned int rst : 1; + unsigned int eak : 1; + unsigned int ack : 1; + unsigned int syn : 1; + unsigned int res : 4; +#else + #error "Must define one of CSP_BIG_ENDIAN or CSP_LITTLE_ENDIAN in csp_platform.h" +#endif + }; + }; + uint16_t seq_nr; + uint16_t ack_nr; +} rdp_header_t; + +/** + * RDP Headers: + * The following functions are helper functions that handles the extra RDP + * information that needs to be appended to all data packets. + */ +static rdp_header_t * csp_rdp_header_add(csp_packet_t * packet) { + rdp_header_t * header = (rdp_header_t *) &packet->data[packet->length]; + packet->length += sizeof(rdp_header_t); + memset(header, 0, sizeof(rdp_header_t)); + return header; +} + +static rdp_header_t * csp_rdp_header_remove(csp_packet_t * packet) { + rdp_header_t * header = (rdp_header_t *) &packet->data[packet->length-sizeof(rdp_header_t)]; + packet->length -= sizeof(rdp_header_t); + return header; +} + +static rdp_header_t * csp_rdp_header_ref(csp_packet_t * packet) { + rdp_header_t * header = (rdp_header_t *) &packet->data[packet->length-sizeof(rdp_header_t)]; + return header; +} + +/* Functions for comparing wrapping sequence numbers and timestamps */ + +/* Return 1 if seq is between start and end (both inclusive) */ +static inline int csp_rdp_seq_between(uint16_t seq, uint16_t start, uint16_t end) { + return (uint16_t)(end - start) >= (uint16_t)(seq - start); +} + +/* Return 1 if seq is before cmp */ +static inline int csp_rdp_seq_before(uint16_t seq, uint16_t cmp) { + return (int16_t)(seq - cmp) < 0; +} + +/* Return 1 if seq is after cmp */ +static inline int csp_rdp_seq_after(uint16_t seq, uint16_t cmp) { + return csp_rdp_seq_before(cmp, seq); +} + +/* Return 1 if time is between start and end (both inclusive) */ +static inline int csp_rdp_time_between(uint32_t time, uint32_t start, uint32_t end) { + return (uint32_t)(end - start) >= (uint32_t)(time - start); +} + +/* Return 1 if time is before cmp */ +static inline int csp_rdp_time_before(uint32_t time, uint32_t cmp) { + return (int32_t)(time - cmp) < 0; +} + +/* Return 1 if time is after cmp */ +static inline int csp_rdp_time_after(uint32_t time, uint32_t cmp) { + return csp_rdp_time_before(cmp, time); +} + +/** + * CONTROL MESSAGES + * The following function is used to send empty messages, + * with ACK, SYN or RST flag. + */ +static int csp_rdp_send_cmp(csp_conn_t * conn, csp_packet_t * packet, int flags, int seq_nr, int ack_nr) { + + csp_id_t idout; + + /* Generate message */ + if (!packet) { + packet = csp_buffer_get(20); + if (!packet) + return CSP_ERR_NOMEM; + packet->length = 0; + } + + /* Add RDP header */ + rdp_header_t * header = csp_rdp_header_add(packet); + header->seq_nr = csp_hton16(seq_nr); + header->ack_nr = csp_hton16(ack_nr); + header->ack = (flags & RDP_ACK) ? 1 : 0; + header->eak = (flags & RDP_EAK) ? 1 : 0; + header->syn = (flags & RDP_SYN) ? 1 : 0; + header->rst = (flags & RDP_RST) ? 1 : 0; + + /* Send copy to tx_queue, before sending packet to IF */ + if (flags & RDP_SYN) { + rdp_packet_t * rdp_packet = csp_buffer_clone(packet); + if (rdp_packet == NULL) return CSP_ERR_NOMEM; + rdp_packet->timestamp = csp_get_ms(); + if (csp_queue_enqueue(conn->rdp.tx_queue, &rdp_packet, 0) != CSP_QUEUE_OK) + csp_buffer_free(rdp_packet); + } + + /* Send control messages with high priority */ + idout = conn->idout; + idout.pri = conn->idout.pri < CSP_PRIO_HIGH ? conn->idout.pri : CSP_PRIO_HIGH; + + /* Send packet to IF */ + csp_iface_t * ifout = csp_rtable_find_iface(idout.dst); + if (csp_send_direct(idout, packet, ifout, 0) != CSP_ERR_NONE) { + csp_log_error("INTERFACE ERROR: not possible to send"); + csp_buffer_free(packet); + return CSP_ERR_BUSY; + } + + /* 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; + +} + +/** + * 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(100); + 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_queue_size(conn->rdp.rx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from rx_queue in queue deliver"); + break; + } + + /* Add seq nr to EACK packet */ + rdp_header_t * header = csp_rdp_header_ref(packet); + packet_eack->data16[packet_eack->length/sizeof(uint16_t)] = csp_hton16(header->seq_nr); + packet_eack->length += sizeof(uint16_t); + csp_log_protocol("Added EACK nr %u", header->seq_nr); + + /* Requeue */ + csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + + } + + return csp_rdp_send_cmp(conn, packet_eack, RDP_ACK | RDP_EAK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + +} + + +/** + * SYN Packet + * The following function sends a SYN packet + */ +static int csp_rdp_send_syn(csp_conn_t * conn) { + + /* Allocate message */ + csp_packet_t * packet = csp_buffer_get(100); + if (packet == NULL) return CSP_ERR_NOMEM; + + /* Generate contents */ + packet->data32[0] = csp_hton32(csp_rdp_window_size); + packet->data32[1] = csp_hton32(csp_rdp_conn_timeout); + packet->data32[2] = csp_hton32(csp_rdp_packet_timeout); + packet->data32[3] = csp_hton32(csp_rdp_delayed_acks); + packet->data32[4] = csp_hton32(csp_rdp_ack_timeout); + packet->data32[5] = csp_hton32(csp_rdp_ack_delay_count); + packet->length = 6 * sizeof(uint32_t); + + return csp_rdp_send_cmp(conn, packet, RDP_SYN, conn->rdp.snd_iss, 0); + +} + +static inline int csp_rdp_receive_data(csp_conn_t * conn, csp_packet_t * packet) { + + /* Remove RDP header before passing to userspace */ + csp_rdp_header_remove(packet); + + /* Enqueue data */ + if (csp_conn_enqueue_packet(conn, packet) < 0) { + csp_log_warn("Conn RX buffer full"); + return CSP_ERR_NOBUFS; + } + + return CSP_ERR_NONE; + +} + +static inline void csp_rdp_rx_queue_flush(csp_conn_t * conn) { + + /* Loop through RX queue */ + int i, count; + csp_packet_t * packet; + +front: + count = csp_queue_size(conn->rdp.rx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from rx_queue in queue deliver"); + break; + } + + rdp_header_t * header = csp_rdp_header_ref(packet); + csp_log_protocol("RX Queue deliver matching Element, seq %u", header->seq_nr); + + /* If the matching packet was found: */ + if (header->seq_nr == (uint16_t)(conn->rdp.rcv_cur + 1)) { + csp_log_protocol("Deliver seq %u", header->seq_nr); + csp_rdp_receive_data(conn, packet); + conn->rdp.rcv_cur++; + /* Loop from first element again */ + goto front; + + /* Otherwise, requeue */ + } else { + csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + } + + } + +} + +static inline bool csp_rdp_seq_in_rx_queue(csp_conn_t * conn, uint16_t seq_nr) { + + /* Loop through RX queue */ + int i, count; + rdp_packet_t * packet; + count = csp_queue_size(conn->rdp.rx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from rx_queue in queue exists"); + break; + } + + csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + + rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *) packet); + csp_log_protocol("RX Queue exists matching Element, seq %u", header->seq_nr); + + /* If the matching packet was found, deliver */ + if (header->seq_nr == seq_nr) { + csp_log_protocol("We have a match"); + return true; + } + + } + + return false; + +} + +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)) + return CSP_QUEUE_ERROR; + return csp_queue_enqueue_isr(conn->rdp.rx_queue, &packet, &pdTrue); + +} + +static void csp_rdp_flush_eack(csp_conn_t * conn, csp_packet_t * eack_packet) { + + /* Loop through TX queue */ + int i, j, count; + rdp_packet_t * packet; + count = csp_queue_size(conn->rdp.tx_queue); + for (i = 0; i < count; i++) { + + if (csp_queue_dequeue(conn->rdp.tx_queue, &packet, 0) != CSP_QUEUE_OK) { + csp_log_error("Cannot dequeue from tx_queue in flush EACK"); + break; + } + + rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *) packet); + csp_log_protocol("EACK compare element, time %u, seq %u", packet->timestamp, csp_ntoh16(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 (csp_ntoh16(eack_packet->data16[j]) == csp_ntoh16(header->seq_nr)) + match = 1; + + /* Enable this if you want EACK's to trigger retransmission */ + if (csp_ntoh16(eack_packet->data16[j]) > csp_ntoh16(header->seq_nr)) { + uint32_t time_now = csp_get_ms(); + if (csp_rdp_time_after(time_now, packet->quarantine)) { + packet->timestamp = time_now - conn->rdp.packet_timeout - 1; + packet->quarantine = time_now + conn->rdp.packet_timeout / 2; + } + } + } + + if (match == 0) { + /* If not found, put back on tx queue */ + csp_queue_enqueue(conn->rdp.tx_queue, &packet, 0); + } else { + /* Found, free */ + csp_log_protocol("TX Element %u freed", csp_ntoh16(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 */ + if (!conn->rdp.delayed_acks) { + return true; + } + + /* ACK if time since last ACK is greater than ACK timeout */ + uint32_t time_now = csp_get_ms(); + if (csp_rdp_time_after(time_now, conn->rdp.ack_timestamp + conn->rdp.ack_timeout)) + return true; + + /* ACK if number of unacknowledged packets is greater than delay count */ + if (csp_rdp_seq_after(conn->rdp.rcv_cur, conn->rdp.rcv_lsa + conn->rdp.ack_delay_count)) + return true; + + return false; + +} + +void csp_rdp_flush_all(csp_conn_t * conn) { + + if ((conn == NULL) || conn->rdp.tx_queue == NULL) { + csp_log_error("Null pointer passed to rdp flush all"); + return; + } + + rdp_packet_t * packet; + + /* Empty TX queue */ + while (csp_queue_dequeue_isr(conn->rdp.tx_queue, &packet, &pdTrue) == CSP_QUEUE_OK) { + if (packet != NULL) { + csp_log_protocol("RDP %p: Flush TX Element, time %u, seq %u", conn, packet->timestamp, csp_ntoh16(csp_rdp_header_ref((csp_packet_t *) packet)->seq_nr)); + csp_buffer_free(packet); + } + } + + /* Empty RX queue */ + while (csp_queue_dequeue_isr(conn->rdp.rx_queue, &packet, &pdTrue) == CSP_QUEUE_OK) { + if (packet != NULL) { + csp_log_protocol("RDP %p: Flush RX Element, time %u, seq %u", conn, packet->timestamp, csp_ntoh16(csp_rdp_header_ref((csp_packet_t *) packet)->seq_nr)); + csp_buffer_free(packet); + } + } + +} + + +int csp_rdp_check_ack(csp_conn_t * conn) { + + /* Check all RX queues for spare capacity */ + int prio, avail = 1; + for (prio = 0; prio < CSP_RX_QUEUES; prio++) { + if (CSP_RX_QUEUE_LENGTH - csp_queue_size(conn->rx_queue[prio]) <= 2 * (int32_t)conn->rdp.window_size) { + avail = 0; + break; + } + } + + /* If more space available, only send after ack timeout or immediately if delay_acks is zero */ + if (avail && csp_rdp_should_ack(conn)) + csp_rdp_send_cmp(conn, NULL, RDP_ACK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + + return CSP_ERR_NONE; + +} + +static inline bool csp_rdp_is_conn_ready_for_tx(csp_conn_t * conn) +{ + // Check Tx window (messages waiting for acks) + if (csp_rdp_seq_after(conn->rdp.snd_nxt, conn->rdp.snd_una + conn->rdp.window_size - 1)) { + return false; + } + return true; +} + +/** + * This function must be called with regular intervals for the + * RDP protocol to work as expected. This takes care of closing + * stale connections and retransmitting traffic. A good place to + * call this function is from the CSP router task. + */ +void csp_rdp_check_timeouts(csp_conn_t * conn) { + + rdp_packet_t * packet; + + /** + * CONNECTION TIMEOUT: + * Check that connection has not timed out inside the network stack + * */ + uint32_t time_now = csp_get_ms(); + if (conn->socket != NULL) { + if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { + csp_log_warn("RDP %p: Found a lost connection (now: %u, ts: %u, to: %u), closing now", + conn, time_now, conn->timestamp, conn->rdp.conn_timeout); + csp_close(conn); + return; + } + } + + /** + * CLOSE-WAIT TIMEOUT: + * After waiting a while in CLOSE-WAIT, the connection should be closed. + */ + if (conn->rdp.state == RDP_CLOSE_WAIT) { + if (csp_rdp_time_after(time_now, conn->timestamp + conn->rdp.conn_timeout)) { + csp_log_protocol("RDP %p: CLOSE_WAIT timeout", conn); + csp_close(conn); + } + return; + } + + /** + * MESSAGE TIMEOUT: + * Check each outgoing message for TX timeout + */ + int i, count; + count = csp_queue_size(conn->rdp.tx_queue); + for (i = 0; i < count; i++) { + + if ((csp_queue_dequeue_isr(conn->rdp.tx_queue, &packet, &pdTrue) != CSP_QUEUE_OK) || packet == NULL) { + csp_log_warn("Cannot dequeue from tx_queue in check timeout"); + break; + } + + /* Get header */ + rdp_header_t * header = csp_rdp_header_ref((csp_packet_t *) packet); + + /* If acked, do not retransmit */ + if (csp_rdp_seq_before(csp_ntoh16(header->seq_nr), conn->rdp.snd_una)) { + csp_log_protocol("TX Element Free, time %u, seq %u, una %u", packet->timestamp, csp_ntoh16(header->seq_nr), conn->rdp.snd_una); + csp_buffer_free(packet); + continue; + } + + /* Check timestamp and retransmit if needed */ + if (csp_rdp_time_after(time_now, packet->timestamp + conn->rdp.packet_timeout)) { + csp_log_protocol("TX Element timed out, retransmitting seq %u", csp_ntoh16(header->seq_nr)); + + /* Update to latest outgoing ACK */ + header->ack_nr = csp_hton16(conn->rdp.rcv_cur); + + /* Send copy to tx_queue */ + packet->timestamp = csp_get_ms(); + csp_packet_t * new_packet = csp_buffer_clone(packet); + csp_iface_t * ifout = csp_rtable_find_iface(conn->idout.dst); + if (csp_send_direct(conn->idout, new_packet, ifout, 0) != CSP_ERR_NONE) { + csp_log_warn("Retransmission failed"); + csp_buffer_free(new_packet); + } + + } + + /* Requeue the TX element */ + csp_queue_enqueue_isr(conn->rdp.tx_queue, &packet, &pdTrue); + + } + + /** + * ACK TIMEOUT: + * Check ACK timeouts, if we have unacknowledged segments + */ + if (conn->rdp.delayed_acks) { + csp_rdp_check_ack(conn); + } + + /* Wake user task if connection is open and additional Tx can be done */ + if ((conn->rdp.state == RDP_OPEN) && csp_rdp_is_conn_ready_for_tx(conn)) { + csp_log_protocol("RDP %p: Wake Tx task (check timeouts)", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); + } +} + +void csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { + + /* Get RX header and convert to host byte-order */ + rdp_header_t * rx_header = csp_rdp_header_ref(packet); + rx_header->ack_nr = csp_ntoh16(rx_header->ack_nr); + rx_header->seq_nr = csp_ntoh16(rx_header->seq_nr); + + csp_log_protocol("RDP %p: Received in S %u: syn %u, ack %u, eack %u, " + "rst %u, seq_nr %5u, ack_nr %5u, packet_len %u (%u)", + conn, conn->rdp.state, rx_header->syn, rx_header->ack, rx_header->eak, + rx_header->rst, rx_header->seq_nr, rx_header->ack_nr, + packet->length, packet->length - sizeof(rdp_header_t)); + + /* If a RESET was received. */ + if (rx_header->rst) { + + if (rx_header->ack) { + /* Store current ack'ed sequence number */ + conn->rdp.snd_una = rx_header->ack_nr + 1; + } + + if (conn->rdp.state == RDP_CLOSE_WAIT || conn->rdp.state == RDP_CLOSED) { + csp_log_protocol("RDP %p: RST received in CLOSE_WAIT or CLOSED. Now closing connection", conn); + goto discard_close; + } else { + csp_log_protocol("RDP %p: Got RESET in state %u", conn, conn->rdp.state); + + if (rx_header->seq_nr == (uint16_t)(conn->rdp.rcv_cur + 1)) { + csp_log_protocol("RDP %p: RESET in sequence, no more data incoming, reply with RESET", conn); + conn->rdp.state = RDP_CLOSE_WAIT; + conn->timestamp = csp_get_ms(); + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + goto discard_close; + } else { + csp_log_protocol("RDP %p: RESET out of sequence, keep connection open", conn); + goto discard_open; + } + } + } + + /* The BIG FAT switch (state-machine) */ + switch(conn->rdp.state) { + + /** + * STATE == CLOSED + */ + case RDP_CLOSED: { + + /* No SYN flag set while in closed. Inform by sending back RST */ + if (!rx_header->syn) { + csp_log_protocol("Not SYN received in CLOSED state. Discarding packet"); + csp_rdp_send_cmp(conn, NULL, RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + goto discard_close; + } + + csp_log_protocol("RDP: SYN-Received"); + + /* Setup TX seq. */ + conn->rdp.snd_iss = (uint16_t)rand(); + conn->rdp.snd_nxt = conn->rdp.snd_iss + 1; + conn->rdp.snd_una = conn->rdp.snd_iss; + + /* Store RX seq. */ + conn->rdp.rcv_cur = rx_header->seq_nr; + conn->rdp.rcv_irs = rx_header->seq_nr; + conn->rdp.rcv_lsa = rx_header->seq_nr; + + /* Store RDP options */ + conn->rdp.window_size = csp_ntoh32(packet->data32[0]); + conn->rdp.conn_timeout = csp_ntoh32(packet->data32[1]); + conn->rdp.packet_timeout = csp_ntoh32(packet->data32[2]); + conn->rdp.delayed_acks = csp_ntoh32(packet->data32[3]); + conn->rdp.ack_timeout = csp_ntoh32(packet->data32[4]); + conn->rdp.ack_delay_count = csp_ntoh32(packet->data32[5]); + csp_log_protocol("RDP: Window Size %u, conn timeout %u, packet timeout %u", + conn->rdp.window_size, conn->rdp.conn_timeout, conn->rdp.packet_timeout); + csp_log_protocol("RDP: Delayed acks: %u, ack timeout %u, ack each %u packet", + conn->rdp.delayed_acks, conn->rdp.ack_timeout, conn->rdp.ack_delay_count); + + /* Connection accepted */ + conn->rdp.state = RDP_SYN_RCVD; + + /* Send SYN/ACK */ + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_SYN, conn->rdp.snd_iss, conn->rdp.rcv_irs); + + goto discard_open; + + } + break; + + /** + * STATE == SYN-SENT + */ + case RDP_SYN_SENT: { + + /* First check SYN/ACK */ + if (rx_header->syn && rx_header->ack) { + + conn->rdp.rcv_cur = rx_header->seq_nr; + conn->rdp.rcv_irs = rx_header->seq_nr; + conn->rdp.rcv_lsa = rx_header->seq_nr - 1; + conn->rdp.snd_una = rx_header->ack_nr + 1; + conn->rdp.ack_timestamp = csp_get_ms(); + conn->rdp.state = RDP_OPEN; + + csp_log_protocol("RDP: NP: Connection OPEN"); + + /* Send ACK */ + csp_rdp_send_cmp(conn, NULL, RDP_ACK, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + + /* Wake TX task */ + csp_log_protocol("RDP %p: Wake Tx task (ack)", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); + + goto discard_open; + } + + /* If there was no SYN in the reply, our SYN message hit an already open connection + * This is handled by sending a RST. + * Normally this would be followed up by a new connection attempt, however + * we don't have a method for signaling this to the user space. + */ + if (rx_header->ack) { + csp_log_error("Half-open connection found, sending RST"); + csp_rdp_send_cmp(conn, NULL, RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + csp_log_protocol("RDP %p: Wake Tx task (rst)", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); + + goto discard_open; + } + + /* Otherwise we have an invalid command, such as a SYN reply to a SYN command, + * indicating simultaneous connections, which is not possible in the way CSP + * reserves some ports for server and some for clients. + */ + csp_log_error("Invalid reply to SYN request"); + goto discard_close; + + } + break; + + /** + * STATE == OPEN + */ + case RDP_SYN_RCVD: + case RDP_OPEN: + { + + /* SYN or !ACK is invalid */ + if (rx_header->syn || !rx_header->ack) { + if (rx_header->seq_nr != conn->rdp.rcv_irs) { + csp_log_error("Invalid SYN or no ACK, resetting!"); + goto discard_close; + } else { + csp_log_protocol("Ignoring duplicate SYN packet!"); + goto discard_open; + } + } + + /* Check sequence number */ + if (!csp_rdp_seq_between(rx_header->seq_nr, conn->rdp.rcv_cur + 1, conn->rdp.rcv_cur + conn->rdp.window_size * 2)) { + csp_log_protocol("Invalid sequence number! %"PRIu16" not between %"PRIu16" and %"PRIu16, + rx_header->seq_nr, conn->rdp.rcv_cur + 1, conn->rdp.rcv_cur + 1 + conn->rdp.window_size * 2); + /* 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; + } + + /* Check ACK number */ + if (!csp_rdp_seq_between(rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1)) { + csp_log_error("Invalid ACK number! %u not between %u and %u", + rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1); + goto discard_open; + } + + /* Check SYN_RCVD ACK */ + if (conn->rdp.state == RDP_SYN_RCVD) { + if (rx_header->ack_nr != conn->rdp.snd_iss) { + csp_log_error("SYN-RCVD: Wrong ACK number"); + goto discard_close; + } + csp_log_protocol("RDP: NC: Connection OPEN"); + conn->rdp.state = RDP_OPEN; + + /* If a socket is set, this message is the first in a new connection + * so the connection must be queued to the socket. */ + if (conn->socket != NULL) { + + /* Try queueing */ + if (csp_queue_enqueue(conn->socket, &conn, 0) == CSP_QUEUE_FULL) { + csp_log_error("ERROR socket cannot accept more connections"); + goto discard_close; + } + + /* Ensure that this connection will not be posted to this socket again + * and remember that the connection handle has been passed to userspace + * by setting the socket = NULL */ + conn->socket = NULL; + } + + } + + /* Store current ack'ed sequence number */ + conn->rdp.snd_una = rx_header->ack_nr + 1; + + /* We have an EACK */ + if (rx_header->eak) { + if (packet->length > sizeof(rdp_header_t)) + csp_rdp_flush_eack(conn, packet); + goto discard_open; + } + + /* If no data, return here */ + if (packet->length <= sizeof(rdp_header_t)) + goto discard_open; + + /* 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) != CSP_QUEUE_OK) { + csp_log_protocol("Duplicate sequence number"); + csp_rdp_check_ack(conn); + goto discard_open; + } + csp_rdp_send_eack(conn); + goto accepted_open; + } + + /* Store sequence number before stripping RDP header */ + uint16_t seq_nr = rx_header->seq_nr; + + /* Receive data */ + if (csp_rdp_receive_data(conn, packet) != CSP_ERR_NONE) + goto discard_open; + + /* Update last received packet */ + conn->rdp.rcv_cur = seq_nr; + + /* Only ACK the message if there is room for a full window in the RX buffer. + * Unacknowledged segments are ACKed by csp_rdp_check_timeouts when the buffer is + * no longer full. */ + csp_rdp_check_ack(conn); + + /* Flush RX queue */ + csp_rdp_rx_queue_flush(conn); + + goto accepted_open; + + } + break; + + case RDP_CLOSE_WAIT: + + /* Ignore SYN or !ACK */ + if (rx_header->syn || !rx_header->ack) { + csp_log_protocol("Invalid SYN or no ACK in CLOSE-WAIT"); + goto discard_open; + } + + /* Check ACK number */ + if (!csp_rdp_seq_between(rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1)) { + csp_log_error("Invalid ACK number! %u not between %u and %u", + rx_header->ack_nr, conn->rdp.snd_una - 1 - (conn->rdp.window_size * 2), conn->rdp.snd_nxt - 1); + goto discard_open; + } + + /* Store current ack'ed sequence number */ + conn->rdp.snd_una = rx_header->ack_nr + 1; + + /* Send back a reset */ + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + + goto discard_open; + + default: + csp_log_error("RDP: ERROR default state!"); + goto discard_close; + } + +discard_close: + /* If user-space has received the connection handle, wake it up, + * by sending a NULL pointer, user-space should close connection */ + if (conn->socket == NULL) { + csp_log_protocol("RDP %p: Waiting for userspace to close", conn); + csp_conn_enqueue_packet(conn, NULL); + } else { + csp_close(conn); + } + +discard_open: + csp_buffer_free(packet); +accepted_open: + return; + +} + +int csp_rdp_connect(csp_conn_t * conn, uint32_t timeout) { + + int retry = 1; + + conn->rdp.window_size = csp_rdp_window_size; + conn->rdp.conn_timeout = csp_rdp_conn_timeout; + conn->rdp.packet_timeout = csp_rdp_packet_timeout; + conn->rdp.delayed_acks = csp_rdp_delayed_acks; + conn->rdp.ack_timeout = csp_rdp_ack_timeout; + conn->rdp.ack_delay_count = csp_rdp_ack_delay_count; + conn->rdp.ack_timestamp = csp_get_ms(); + +retry: + csp_log_protocol("RDP %p: Active connect, conn state %u", conn, conn->rdp.state); + + if (conn->rdp.state == RDP_OPEN) { + csp_log_error("RDP %p: Connection already open", conn); + return CSP_ERR_ALREADY; + } + + /* Randomize ISS */ + conn->rdp.snd_iss = (uint16_t)rand(); + + conn->rdp.snd_nxt = conn->rdp.snd_iss + 1; + conn->rdp.snd_una = conn->rdp.snd_iss; + + csp_log_protocol("RDP %p: AC: Sending SYN", conn); + + /* Ensure semaphore is busy, so router task can release it */ + csp_bin_sem_wait(&conn->rdp.tx_wait, 0); + + /* Send SYN message */ + conn->rdp.state = RDP_SYN_SENT; + if (csp_rdp_send_syn(conn) != CSP_ERR_NONE) + goto error; + + /* Wait for router task to release semaphore */ + csp_log_protocol("RDP %p: AC: Waiting for SYN/ACK reply...", conn); + int result = csp_bin_sem_wait(&conn->rdp.tx_wait, conn->rdp.conn_timeout); + + if (result == CSP_SEMAPHORE_OK) { + if (conn->rdp.state == RDP_OPEN) { + csp_log_protocol("RDP %p: AC: Connection OPEN", conn); + return CSP_ERR_NONE; + } else if(conn->rdp.state == RDP_SYN_SENT) { + if (retry) { + csp_log_warn("RDP %p: Half-open connection detected, RST sent, now retrying", conn); + csp_rdp_flush_all(conn); + retry = 0; + goto retry; + } else { + csp_log_error("RDP %p: Connection stayed half-open, even after RST and retry!", conn); + goto error; + } + } + } else { + csp_log_protocol("RDP %p: AC: Connection Failed", conn); + goto error; + } + +error: + conn->rdp.state = RDP_CLOSE_WAIT; + return CSP_ERR_TIMEDOUT; + +} + +int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout) { + + if (conn->rdp.state != RDP_OPEN) { + csp_log_error("RDP: ERROR cannot send, connection reset"); + return CSP_ERR_RESET; + } + + while ((conn->rdp.state == RDP_OPEN) && (csp_rdp_is_conn_ready_for_tx(conn) == false)) { + csp_log_protocol("RDP %p: Waiting for window update before sending seq %u", conn, conn->rdp.snd_nxt); + if ((csp_bin_sem_wait(&conn->rdp.tx_wait, conn->rdp.conn_timeout)) != CSP_SEMAPHORE_OK) { + csp_log_error("RDP %p: Timeout during send", conn); + return CSP_ERR_TIMEDOUT; + } + } + + if (conn->rdp.state != RDP_OPEN) { + csp_log_error("RDP: ERROR cannot send, connection reset"); + return CSP_ERR_RESET; + } + + /* Add RDP header */ + rdp_header_t * tx_header = csp_rdp_header_add(packet); + tx_header->ack_nr = csp_hton16(conn->rdp.rcv_cur); + tx_header->seq_nr = csp_hton16(conn->rdp.snd_nxt); + tx_header->ack = 1; + + /* Send copy to tx_queue */ + rdp_packet_t * rdp_packet = csp_buffer_clone(packet); + if (rdp_packet == NULL) { + csp_log_error("Failed to allocate packet buffer"); + return CSP_ERR_NOMEM; + } + + rdp_packet->timestamp = csp_get_ms(); + rdp_packet->quarantine = 0; + if (csp_queue_enqueue(conn->rdp.tx_queue, &rdp_packet, 0) != CSP_QUEUE_OK) { + csp_log_error("No more space in RDP retransmit queue"); + csp_buffer_free(rdp_packet); + return CSP_ERR_NOBUFS; + } + + csp_log_protocol("RDP: Sending in S %u: syn %u, ack %u, eack %u, " + "rst %u, seq_nr %5u, ack_nr %5u, packet_len %u (%u)", + conn->rdp.state, tx_header->syn, tx_header->ack, tx_header->eak, + tx_header->rst, csp_ntoh16(tx_header->seq_nr), csp_ntoh16(tx_header->ack_nr), + packet->length, packet->length - sizeof(rdp_header_t)); + + conn->rdp.snd_nxt++; + return CSP_ERR_NONE; + +} + +int csp_rdp_allocate(csp_conn_t * conn) { + + csp_log_protocol("RDP: Creating RDP queues for conn %p", conn); + + /* Set initial state */ + conn->rdp.state = RDP_CLOSED; + conn->rdp.conn_timeout = csp_rdp_conn_timeout; + conn->rdp.packet_timeout = csp_rdp_packet_timeout; + + /* Create a binary semaphore to wait on for tasks */ + if (csp_bin_sem_create(&conn->rdp.tx_wait) != CSP_SEMAPHORE_OK) { + csp_log_error("Failed to initialize semaphore"); + return CSP_ERR_NOMEM; + } + + /* Create TX queue */ + conn->rdp.tx_queue = csp_queue_create(CSP_RDP_MAX_WINDOW, sizeof(csp_packet_t *)); + if (conn->rdp.tx_queue == NULL) { + csp_log_error("Failed to create TX queue for conn"); + csp_bin_sem_remove(&conn->rdp.tx_wait); + return CSP_ERR_NOMEM; + } + + /* Create RX queue */ + conn->rdp.rx_queue = csp_queue_create(CSP_RDP_MAX_WINDOW * 2, sizeof(csp_packet_t *)); + if (conn->rdp.rx_queue == NULL) { + csp_log_error("Failed to create RX queue for conn"); + csp_bin_sem_remove(&conn->rdp.tx_wait); + csp_queue_remove(conn->rdp.tx_queue); + return CSP_ERR_NOMEM; + } + + return CSP_ERR_NONE; + +} + +/** + * @note This function may only be called from csp_close, and is therefore + * without any checks for null pointers. + */ +int csp_rdp_close(csp_conn_t * conn) { + + if (conn->rdp.state == RDP_CLOSED) + return CSP_ERR_NONE; + + /* If message is open, send reset */ + if (conn->rdp.state != RDP_CLOSE_WAIT) { + conn->rdp.state = RDP_CLOSE_WAIT; + conn->timestamp = csp_get_ms(); + csp_rdp_send_cmp(conn, NULL, RDP_ACK | RDP_RST, conn->rdp.snd_nxt, conn->rdp.rcv_cur); + csp_log_protocol("RDP %p: Close, sent RST", conn); + csp_bin_sem_post(&conn->rdp.tx_wait); // wake up any pendng Tx + return CSP_ERR_AGAIN; + } + + csp_log_protocol("RDP %p: Close in CLOSE_WAIT, now closing", conn); + conn->rdp.state = RDP_CLOSED; + return CSP_ERR_NONE; + +} + +/** + * RDP Set socket options + * Controls important parameters of the RDP protocol. + * These settings will be applied to all new outgoing connections. + * The settings are global, so be sure no other task are conflicting with your settings. + */ +void csp_rdp_set_opt(unsigned int window_size, unsigned int conn_timeout_ms, + unsigned int packet_timeout_ms, unsigned int delayed_acks, + unsigned int ack_timeout, unsigned int ack_delay_count) { + csp_rdp_window_size = window_size; + csp_rdp_conn_timeout = conn_timeout_ms; + csp_rdp_packet_timeout = packet_timeout_ms; + csp_rdp_delayed_acks = delayed_acks; + csp_rdp_ack_timeout = ack_timeout; + csp_rdp_ack_delay_count = ack_delay_count; +} + +void csp_rdp_get_opt(unsigned int * window_size, unsigned int * conn_timeout_ms, + unsigned int * packet_timeout_ms, unsigned int * delayed_acks, + unsigned int * ack_timeout, unsigned int * ack_delay_count) { + + if (window_size) + *window_size = csp_rdp_window_size; + if (conn_timeout_ms) + *conn_timeout_ms = csp_rdp_conn_timeout; + if (packet_timeout_ms) + *packet_timeout_ms = csp_rdp_packet_timeout; + if (delayed_acks) + *delayed_acks = csp_rdp_delayed_acks; + if (ack_timeout) + *ack_timeout = csp_rdp_ack_timeout; + if (ack_delay_count) + *ack_delay_count = csp_rdp_ack_delay_count; +} + +#ifdef CSP_DEBUG +void csp_rdp_conn_print(csp_conn_t * conn) { + + if (conn == NULL) + return; + + printf("\tRDP: State %"PRIu16", rcv %"PRIu16", snd %"PRIu16", win %"PRIu32"\r\n", + conn->rdp.state, conn->rdp.rcv_cur, conn->rdp.snd_una, conn->rdp.window_size); + +} +#endif + +#endif diff --git a/gomspace/libgscsp/lib/libcsp/src/transport/csp_transport.h b/gomspace/libgscsp/lib/libcsp/src/transport/csp_transport.h new file mode 100644 index 00000000..7fcda3dc --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/transport/csp_transport.h @@ -0,0 +1,46 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _CSP_TRANSPORT_H_ +#define _CSP_TRANSPORT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** ARRIVING SEGMENT */ +void csp_udp_new_packet(csp_conn_t * conn, csp_packet_t * packet); +void csp_rdp_new_packet(csp_conn_t * conn, csp_packet_t * packet); + +/** RDP: USER REQUESTS */ +int csp_rdp_connect(csp_conn_t * conn, uint32_t timeout); +int csp_rdp_allocate(csp_conn_t * conn); +int csp_rdp_close(csp_conn_t * conn); +void csp_rdp_conn_print(csp_conn_t * conn); +int csp_rdp_send(csp_conn_t * conn, csp_packet_t * packet, uint32_t timeout); +int csp_rdp_check_ack(csp_conn_t * conn); +void csp_rdp_check_timeouts(csp_conn_t * conn); +void csp_rdp_flush_all(csp_conn_t * conn); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _CSP_TRANSPORT_H_ */ diff --git a/gomspace/libgscsp/lib/libcsp/src/transport/csp_udp.c b/gomspace/libgscsp/lib/libcsp/src/transport/csp_udp.c new file mode 100644 index 00000000..61732703 --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/src/transport/csp_udp.c @@ -0,0 +1,49 @@ +/* +Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include "../csp_port.h" +#include "../csp_conn.h" +#include "csp_transport.h" + +void csp_udp_new_packet(csp_conn_t * conn, csp_packet_t * packet) { + + /* Enqueue */ + if (csp_conn_enqueue_packet(conn, packet) < 0) { + csp_log_error("Connection buffer queue full!"); + csp_buffer_free(packet); + return; + } + + /* Try to queue up the new connection pointer */ + if (conn->socket != NULL) { + if (csp_queue_enqueue(conn->socket, &conn, 0) != CSP_QUEUE_OK) { + csp_log_warn("Warning socket connection queue full"); + csp_close(conn); + return; + } + + /* Ensure that this connection will not be posted to this socket again */ + conn->socket = NULL; + } + +} + diff --git a/gomspace/libgscsp/lib/libcsp/utils/cfpsplit.py b/gomspace/libgscsp/lib/libcsp/utils/cfpsplit.py new file mode 100644 index 00000000..9a350e3e --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/utils/cfpsplit.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +# Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +# Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Split CFP header in protocol fields + +import sys + +def usage(): + print("usage: cfpsplit.py HEADER") + +def main(): + try: + hdr = sys.argv[1] + except: + usage() + sys.exit(-1) + + try: + hdrhex = int(hdr, 16) + except: + print("HEADER must be in hexadecimal format") + sys.exit(-1) + + if hdrhex > 0x1fffffff: + print("HEADER is not a valid CFP header") + sys.exit(-1) + + print("Source: {0}".format((hdrhex >> 24) & 0x1f)) + print("Destination: {0}".format((hdrhex >> 19) & 0x1f)) + print("Type: {0}".format("MORE" if ((hdrhex >> 18) & 0x01) else "BEGIN")) + print("Remain: {0}".format((hdrhex >> 10) & 0xff)) + print("Identifier: {0}".format((hdrhex >> 0) & 0x3ff)) + +if __name__ == "__main__": + main() diff --git a/gomspace/libgscsp/lib/libcsp/utils/cspsplit.py b/gomspace/libgscsp/lib/libcsp/utils/cspsplit.py new file mode 100644 index 00000000..f4ed942f --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/utils/cspsplit.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +# Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +# Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Split CSP header in protocol fields + +import sys + +def usage(): + print("usage: cspsplit.py HEADER") + +def main(): + try: + hdr = sys.argv[1] + except: + usage() + sys.exit(-1) + + try: + hdrhex = int(hdr, 16) + except: + print("HEADER must be in hexadecimal format") + sys.exit(-1) + + print("Priotity: {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)) + print("Source port: {0}".format((hdrhex >> 8) & 0x3f)) + print("HMAC: {0}".format("Yes" if ((hdrhex >> 3) & 0x01) else "No")) + print("XTEA: {0}".format("Yes" if ((hdrhex >> 2) & 0x01) else "No")) + print("RDP: {0}".format("Yes" if ((hdrhex >> 1) & 0x01) else "No")) + print("CRC32: {0}".format("Yes" if ((hdrhex >> 0) & 0x01) else "No")) + +if __name__ == "__main__": + main() diff --git a/gomspace/libgscsp/lib/libcsp/waf b/gomspace/libgscsp/lib/libcsp/waf new file mode 100644 index 00000000..4b322f1a --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/waf @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# encoding: ISO8859-1 +# Thomas Nagy, 2005-2016 + +""" +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +""" + +import os, sys, inspect + +VERSION="1.8.19" +REVISION="b1fc8f7baef51bd2db4c2971909a568d" +GIT="22213cd8abbd141bda40667f7ca2a48f2d6ad785" +INSTALL='' +C1='#5' +C2='#/' +C3='#,' +cwd = os.getcwd() +join = os.path.join + + +WAF='waf' +def b(x): + return x +if sys.hexversion>0x300000f: + WAF='waf3' + def b(x): + return x.encode() + +def err(m): + print(('\033[91mError: %s\033[0m' % m)) + sys.exit(1) + +def unpack_wafdir(dir, src): + f = open(src,'rb') + c = 'corrupt archive (%d)' + while 1: + line = f.readline() + if not line: err('run waf-light from a folder containing waflib') + if line == b('#==>\n'): + txt = f.readline() + if not txt: err(c % 1) + if f.readline() != b('#<==\n'): err(c % 2) + break + if not txt: err(c % 3) + txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) + + import shutil, tarfile + try: shutil.rmtree(dir) + except OSError: pass + try: + for x in ('Tools', 'extras'): + os.makedirs(join(dir, 'waflib', x)) + except OSError: + err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) + + os.chdir(dir) + tmp = 't.bz2' + t = open(tmp,'wb') + try: t.write(txt) + finally: t.close() + + try: + t = tarfile.open(tmp) + except: + try: + os.system('bunzip2 t.bz2') + t = tarfile.open('t') + tmp = 't' + except: + os.chdir(cwd) + try: shutil.rmtree(dir) + except OSError: pass + err("Waf cannot be unpacked, check that bzip2 support is present") + + try: + for x in t: t.extract(x) + finally: + t.close() + + for x in ('Tools', 'extras'): + os.chmod(join('waflib',x), 493) + + if sys.hexversion<0x300000f: + sys.path = [join(dir, 'waflib')] + sys.path + import fixpy2 + fixpy2.fixdir(dir) + + os.remove(tmp) + os.chdir(cwd) + + try: dir = unicode(dir, 'mbcs') + except: pass + try: + from ctypes import windll + windll.kernel32.SetFileAttributesW(dir, 2) + except: + pass + +def test(dir): + try: + os.stat(join(dir, 'waflib')) + return os.path.abspath(dir) + except OSError: + pass + +def find_lib(): + src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) + base, name = os.path.split(src) + + #devs use $WAFDIR + w=test(os.environ.get('WAFDIR', '')) + if w: return w + + #waf-light + if name.endswith('waf-light'): + w = test(base) + if w: return w + err('waf-light requires waflib -> export WAFDIR=/folder') + + dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) + for i in (INSTALL,'/usr','/usr/local','/opt'): + w = test(i + '/lib/' + dirname) + if w: return w + + #waf-local + dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) + w = test(dir) + if w: return w + + #unpack + unpack_wafdir(dir, src) + return dir + +wafdir = find_lib() +sys.path.insert(0, wafdir) + +if __name__ == '__main__': + + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + +#==> +#BZh91AY&SYmEõKQ#/ÿÿÿ°#,Óÿÿÿÿÿÿÿÿÿÿÿ„ Y Â#%H4#,`(bÜrû}Ñ#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,ÕÓ­ul]ZåÍë}i{l4­´ï±®í_pÝlkLϱ÷dzvúݺ{¾]òxì¾ó¥Ô´©…Rúøq楯½¯y\ÃÛe… h«Ì÷g¡éEm.Ç»u­w¦×ºÔë]2]ôõyk=·{»º¼å·[ž®×™½×ywÞ@=ggÅíkWe÷œ}}À}ìowÕãNzî#,#,#,è#, yì#,x->€è#,ûØÜ<¼À*©©{¸WLåÜû·a¦ƒOl¥Ø5nûÛ§FƒËÊ#,·m6Ð ØÑÛPP½ºu(QBª#5 JJöeGc@#,¤’ P6bªîàïA—nâûîׯZ>ÝÃãH½ž'»{´½íp#/ÍKØÔvPÊZ6îîûÞtÃjÒ>©·Þ·£¾{šûzèÓÝl›»_7Zíôìíëéß}ÙÛ¡¦MZûÚõlíζˮ83oM<€k=bWGÓªU¤Ž rz5O{½ÏyÞå뽇 ôz#/o3ÕPKh—F;ÛÝæ·Ûl5­#5ªvê½Ù¦‡¯f­î{©%žöëÕŽÚ»sg¼RÃË’î}¹Ÿ}ÞÐ#/#//·ZÅ"õîiµÍÀŸç@[ßsÇ­·®Øèì^s +ŽÞ€>û½îBqŸ*ÓKïxÓG5_/»=ÝÐ=ª£¾Ü'Ø}Â>9¶¦¶ë¯½÷e…åÏ6íëžÝÓmõvuÛnúåëÎãï{›E³{«·6·u.#/Ýö»ÎÛêï·îNî»^FM>ŽO}¤ÓˆßX¥­áÍL|R¡ZÛu.9ñ.÷;ìí«/nûÝÞÛ¯7rúçµj;<è¼ßF¾ù¹¾öogƒŸnžïj½zÛ|`uï¾uòú-íèúy=gÔ#, J=°#@˜S{0)ÚRO½Ôó5^‡¯^ñN²>·3îeÝÖk|½ÚðöP•Ü§;{¸Š‰ïUíØ)¨+×Kßw^|#,=Üu #,=ïŸ^—|ívõÞ§Ãv¾Ÿqw{ß]8¾ŽtÓz#¸§í×HÛ˜×@ÓÄåÎÙì^vÛlÒÈλ÷¼zÞ‹›g¾|å6ÝÛÀ%W ¢ëî»ïW×ݶûêx};ìßešô>æ&¾Ž{o=—Þ}î»{¶oŸkÛÝÞ:Þ{}»‡ÛkL}½>û>ya—Û½¾vòÛÀÖŠï°£®šqM_^/…ó:è5í„éx_-ê»IÚžÛíïynOµ@}o³+LUS¯4ºŸZ:û=àuîžæî­a+ïW{ëS¥¾ofw[{½ï\î7Ïvò_|î÷\VŠ€:Õ/±¡Û̳ÏnR„+Åw¨Ñ†#/×_wÑãÛ@mg #,#,ùíÜ:;¶5èkGv»sÉ®Wß"}½Àt@õïxõõèÞê“6ù·wuÏN×Vvû¤½­Îíº‹F’HÍÎ.î‰õ‡Sy½çUr^ßN/FϧÝîO®·Ð¦çq«^;N!ßyNû¬·£à´Ä“NŽí¸{›ovŒkwV¾]ÜÚÛv™òzÉ÷ßskí´ÙNÛ®Ûo6¾÷ÏSæo‡#|w M @#,É Ó#/ ™iODžˆ¥=CjA‰‘êx JhD@„Äi2hÔj6™Sð£D4òÓ'¨#,#,#,4#,#,A!! A¢bh' Sòh ž©6Dõ='©é6 Ð7©#,#,#,#,#,'ªQI4j<ši‰¤ôSf”ôÑ=OHz@#/#,Ш#,3I¡ #,#,#,#,$ˆ#, i0M#,§¡¦‰¦"{I“I‚O$z4Ð#,#,#,#,j"#, Ñ #5™é=SCÑ<©ížò‡Šzž§ê˜€ Ð#,È×ý+m«ÿîbcú†Ö¹µ¹uÉ~ÆÛS°“)#5=¶®º3&ª-5³åV­WêêmV¾Èþ#òþ'—òüµû #Îo{ÉÃMòÎg5ÌÇœ¦o9­Ì¦ç.înç9£šs-Ïð´èJ}B-~¨Z¯¦ÛRªßkæÈD&Tƒ%ŠªÆ(°xT)U\óâãóÏEÓ<š7^øIË̶ëXI³&>rµŒÕÞVª«L*ØiH ?#/ÕõŠ7ÀR ÀYeË©jÒµZ*Ú1µm#,-ÕB¨IX(¤È‚ ‚¡!„[*E±#,|à…R‚ Õ F±V«m¯¿kjµL‚@¡ ı™¦ &D4c6™QSR%Œ‰%(ÒŠm¤¨EL’X XMSQT”DJ “4M”-”ÆJÌ £I,hM0¥F’)6"ÑJmIJZS"Ô@ %–’#5’`˜2’)lQm“T#/A”²¢–&¥()%Ò‰Yš‹F¢´«iU©I6¢dÌ™4lUZ•¬f˜fÌÓ6¤±šm°KM±5-M2jadS$š djF•¤ÖB*M&ƒL’ZH-¦±’²h˜!`£3M1šlb`•4°HA˜†31#/†I‘(‰(l›R@‘KŠÉ¡’#2‘„–f–)2A´˜c%4–ÒTZ#/¦5*ECI†Š4Òl(’Q&È¥±1Pl™M¢’e¤2’&ƒF”ŒÉRŠ)™6ÈT2(–E’BH…6"ÒX5 ¨…"J*5D”…3b $Ô–)1H‰”Œd™3Fa-™S ØJ‚ˆ‰!*M‰ ’M%`Ä”–,³0lÚ#M#FÂJ!e$ ¤Í‘)š¥›,¥Ë0É¥’ji”h#/¥ŠK264ed&bIZ––HXCeA¦Ì Æ£f‘±†SKS[M†!3H¢ÌDjL”š@ÓIK-,©QQD‚Š#/‚&$Ȉ’”Æ4 #$4ÌP¢!•3-¤EaJ¤!”©&B”…6’ÒQl‘¬i&I% E’šÈ¶F̆LцÉ&Ê“L¦¢1Ë*™¥1 P©“DTѳSb“(²2bJE*I!e2,Ji‚´¤É²’f&É£&B¡¤`B5$Ë)YI†ÐY6J@“!%“(ÈE3A’2™HË0–’•Mš*6‹BeÈ”‘ÔbØÒLÖE#/&60’FTi#/*%k,˜Q¬eš™"f‹&–DJÅ,€5JJ2ŒÒ5µ6Ûh(¶0IS@´”ÊHÑb™)’cI£l–”³F˜¤Ìe3K*M”±jdcal’#5›(ÊFhÓe¯Úmv4…•¤¤%J2SX«EbÑQ²IšTDÓ%KAQ°Š’V¦"†Æ–!™e) RF³*Ô&Ú,Šk&Cc)–•E¡bd¦5-*c$&,32•Y£e”#k*ER©X¦Ê$²£)µdÔˆÖɦVD©5 [%YYJSL+f(i‘5£KMD–’%ƱXÕ3h¶ÛH3`±ªÂF¤Ñ¶Æѱ²Qª3LÄ5E!¨4jÅQ°ÍHmbY0¢5³Rhe1%‚h±„5(”Ú–Äm$šÆ5!b´’šµ³VÓA,#ei6F&³l‰3)©¥#5I–Õ6¢–’”Ô²¤¶š¦ÒZ¦˜“²¦Rɶ–m¦ÌZ4f¶k32I¬±¡›¤ÛKYRSJ(ˆ C5J4‰ŒQ°b„- 3–ѵ$JME2š£iI¥FLš#/%0ŠE,¨ŠAieH€i¢˜)’e6hbÄ„¤TZ,É b•š’ÀclË3K1š`©¨™Ñ2FLlBÌK!I´lI¤£&h4‹2‹S2TÅ4Rma3`Ñ#5TXÌ1S6 RÉBlE†54Ú3 3h¨±±±F„hÆÉ©¡"“aK-“$¤¦L¦Á’Ø…3R‰2I²ÒˆJ¨²Ô™¢6”ÒlÆÒei+Ä#”dfM”“BTš“)FÌÓj[,Ø´”³I²lbTQfŠJfdÑ©–´Œ”¥¢ÁH‘›$FY‘1¢Òš#@ÖY6Jhbb&“I F¢5BTmmˆ­1¤ÃI2ÄÊ &‘h¦•É K#5¥e$š *5“0²ie24´›mIkhЙfÚ‚H´–ÅA£ ,#/›R”JQliIš¨Ù&k2*H’£I"Ë*ÔƆMEc&&VJˆ¦”¡Q¥%4ET±*Je)FÛ`µ$Qd£dª0Ê6Kd¤Ô›j”ÑIEJjµ&ÅCI3d†RTXiH‹- ””ÆhÁ´˜#5‰3BÁª”FÈÊV&#JKEhƲEd„6%`Š1”¶#h¬¤”T€h1E& “V”ZST¦Òj-±¬VÍ1Y-f¢©š’‹Å(#(1³B$¡4l”Á£VÄm‹hØÒšÖHe¢#5Æ&¢Ñ­6Ë6£h´TZÆI5©fÍÒÊĪ1h³,UMIHÛ(Ë)DÒFÊÅ`’Æ’Ù-d¢Ô–±I´¦eM¢Â‘QIF‹kT­´ŒeCI¢$©”Z66† ÒZ[$ÛIŒÉl‚›%Eµ$¶ŠÑ­‹FÛhÚ¡K&ÖÄZ¦¥e´J2ŠbFÒL`©lMšlT‘²È¨ ™‰1$™I2$¡jM±fV¦?‹çý5üKý&¼ |ü©ŒRžÜÐZdI‹©±‡þå©-Iû²Ò/E¹±ò»´–^ï|¿¦¿¤ë×ô£Æ°Tԥƪ?ðÖã,Sý^‰b¨ O¦ò¢ób¬M4ºd7%‚•Ä3³m´5T;%ëvþ>zãyg÷îÐÿƒÌ³'ù£Ÿ,¯uc/eÕLŠYÝ]ÈZ1±, òt–u÷ë-²'«êÆÚe)‰QéjoÎQmå¦Ðn¥ØГfN½1j—qÈqÖ=c,„Â’¦6Ýr¸6=)`ó¨S‚¢5Š¨ªìÃKJÈÒ|9ëW®Y×[#¿Êní·ei”-M(¥@Ñ ¸_ñQ eRËFƶh&«ìºòO9Q ‰¸‘6Y6?óÀÝ€4Í5ȤU5vZ±ËQ­îX?ùhÿÀ¦ãaRµ^Ô›2Æ*Šžê2ØÁdX¿ë¹aßÝZ3#,©Ñ#57ªAOS(C (`)UAÒ¼®—é­u›ërÖæå‹òöêÞKŠež f>ÐD¹Ó§‘&²–9©%C™E¤¹æ%Š¿žªùOUòmº˜šƒ@ìQ†EÇÛ{zY]J4}]¢­0n(i¥JÂn@rpeË’nºìNºéŠƒ}K[ËÎÿlàUŠ ,D¬ÐÑ(Fí¶R·-"‚¿MTFzè­UiŽ´ÿÚz`™`ÿ¦ˆÎ™O-Li6ÂJ°¹@`@hÔ¨OÐVâ<çÕ€˜Ú˜Dã™{Œ’àq¶Ü`Cò“U}Ã]¿l]~>îÙ›ˆ­Vpò…§†o‘;J0Û)¨ãjåýeNôÕ8TãLhµÅ©j¶0æCfB(t\´Ã·µö% v3IêåS—ʉf´0 ÷09!àá­ñÁ±´µºÎš‰™Ú†{¦’z XZ%l²ÜÝ(ÂOa"ÚO– ½:Þ>×åŠ0ö@¨ÎÚÑfÉPe‰J#5WïÉ—RcäÝ¿£ZÁ6–‘»GÕ"_ºø7Ýv›$£#}þ»_zîF: ãe.¶W7"^*®P‹ÿ‰¥ð$cós´WŽLŽVÚiþÎУ²?.ãä]u6Š1£^9™dÞ5Ë–Æ«ù)¨Óï84mëwßêü¯ ™þº¾ïó{_Kâíå¹£UÝ\XŒQd$Äa–“Þ뻩 (hdS£C"¬éÊŒ&/º_†W x«¦¬oW“ñ{ÑJ_7*#5¾óáy›â¸ÌWíMu­»4 ‰"1SJBÌV·b·cX²júOöÅòW#kðº„"ȆùkÁ‡ÏPÐ?íV"0ãKŸEÓlØþz'<• "1Ò¶³d< ™!µõwë¼®mÔß9u#Es”H²sª›»Œ…¦õAu¢–•­Bz’ZÅŽ£”ÈP‚wQ&šÒ›³äšêP¨¬¢ÚTD×¾ï˜XùíÏIà•½raißÝv‘’dªFÓÊÊ90Õ L"ÈòªKv‰ÑÄ®áçˆ6ÊÃm-8ÂÉ2ÔQ¶Ùìᦻ`¶"§#5ôºœûªŒÀ(ò@Þø%€.ê}UÁ b‡dtAí²—ðŒ´Ö3Úá·0÷"+„úÇitˈ!cûœøÏE0Œ«“ꆀáÇ6¿v¹ÎÞ,MP—“$XŠ‚Œb‚ ÝT‚}åuÒV …H±CZ*SHuÅqf?áNåyaðÚ»™:¡¨ã…ufÿ/jiÉÐ80jN §Hï¯Öo/žø9`Î>®¿/,šÁ)Bµ)ÍA¿:—+«v#W*ƒÆ|™u ²&7½DjBQN84ÛüÕK°™&d¦«ý#5*mÏí¯gÓ|¸ÕÍŸQNoø (|‹¡Ù(ÅsÛ_á|¼»ðÄV'rï¾I3Æm¼c+¯-ÍIŸEMT}[×@0ØÏÂŽì$‰hJº¯ZÅX¿|ê*—º&dìFŽÚmÄŽÈŽ0¡üGʯg™kú‰;¼e ÷›¤x Á×ÇœønãoîqØàäñÕOì´÷¿.w’ô‡ž˜7p’3Œ¶=æF¶÷¿½P'ãûµ†HùOÀÌhËhÕq¦{æ2°oïüÒ¿„ѯ%BÒ„–¨¯:?_ÛëñͺC—öGÊŠo‡®.W’wD+:Äy/²"]žòæO{{õ•X*Rpª‘ëkq*(øV1)"­¯MëÝ´ò‹ýfŠì†˜­PcMïôå1­AÄ«pÒ¤V=§Aç=ÓFÐì¾ì¿n.ØÜ=oeŽH!aÝ$™hNü|dm«]-üÑ”ÈLÛ@áò:<¥—¤!iõ—o »h-ˆHAWã‰ûs2±ÃpTªj¹î]¬ð«³ƒ-öN˜H”Š#5Ft—æ¸>ªµ¨;ùÖ”ú?ÝŒv3ÜÜ¥D#PübžÐÓ®ÏZ:Ö4@Iî¥*h­=(<—Ü+Ã4—|õ#5ÇÅþŸšµB—_xá Ÿ>Ä4¯]ÀG§H­Ñ Œ|]#5b"«£.xù›ã•#/.SθÊîta¦aæ”ÚØ”†•7lDݶØ1Õmð¿þü,8HÑ“èºr.ÿ›>y#/‘A;÷Éí âo͘«HjEá¤Aƒhlù½W—â(¥ôvÝë\—évê0‚1:éeˆ9Jö0¯¢ÑG’·I”Ü•óåþÕéçþ§[Oïï¼û6±º{0)º¡J‰jUª×’ÒÙ.éaðg¸müÌAÖg­@I#5ÇBÞ‰¼`ÑG¡àaé-†{_#/™[á%Îs¶­OÏ>Ñ»5²F3Ù1õ «8 9ºùý71ÝF¿m€§zä¦(§-|4°²øbÌdj~.£áøQ’ʤX€Š=_©žoŽxpîð§çÌU5øøïWìýÊ,饌„{‰#ŒÖ³Õ'‘Tiç¯<P8°)‰:YßmMî£b‰w@ ‚Á@a”ãƒÓŒ=x³Õ¥dÀ}øëy”Ð+MuÞøb…#5nê3›°èM6~ŠR-äœÙ¡‡Žó\æòx@ëpÔ8\Ê6ir¢t{î¦P¤ª¡AFšòh„»ÜäSC°¡ã1ºb™RüŽÓ`^©š;4õªâÍl)W»µZ›ívE€Àe³CÌL¢ˆ²šèóª¤;&#/F‘Ò§»t^yyÊxq¦.K•ö3¸ë·á˜ËÌèQIŒ:d$Z¨g§7‹ ”†B©É=0JŠBJq¢}öÉé†>4ßi¶DÛ%RR„Ç’Â"[àCþõçoqE1×íàä‚ØáÚ{~Yƒ>«ÉökZ™;xZoÚ¤Lhø‰êÂ×VRŠÄ9³HÉ`œMuôÆÅxë²7cÌOrJD2ÇÓ: uÁv˜ôOîrÊêe“¦DeÿØ$Ï:3Îw׈X© *B²‹Ÿ,žöJø¾â¡œð½Úõˆ^ï0s‡wÒ‚åÓX‹F¶;¢ûn^Ÿß‡Ž›\ÍÈT#5p€Lt"Ý3ÃtíÝé8§Ç§„ÊóÑšMÀ¨§ƒHœ(£1ÅBP0ŒA¢Äßøºj¯f¯†§…Ó»}s[Õ{C©Ý}ÿ>Ï7M/e«å-o‘Œîe‡#5#5Ö¼ ¤dSRª{^{÷ê}i›ƒ¤#,$ ¡NjG÷G5íIÉOµÇÔ?Ë–ç§WègLäãóg]EÇòü*Ô;Þþù» ðzþ¨/o/b‘œwÒA«^?%g%ÛÓê÷ªzn©÷|ö.?#×}$éäüq=^E¤nñí¥¤Ö ìFcïv–f¦¶,ù#/³,ðgîg›œ!³¾#5~Š«ªPžâiå:: Ðš:䲦‰ïQi±WýÐÊ9ªÃÝkdUWk¢™B#/IMvýõgØøé(:0Ì"ðp8®H±/Fý“à@ô:Jæg‘ÿºÃª Uüבä¢e;tÊ™á‡óúÎ3†ÙöÛ¹ðÐC~ÛÍêÜôzM,„ÂçöÂÁT®œw6—j†)Ú!¥kûçövñ|­Cï£ÒPðˆ—.QËËÀGòB|‚rÆ=*D2•þ,Ñ®wÀ ÊÈßà›3•ÏÏÒìâý©ÕÈá´J?JwHMÂSÖ¥ÒXTñj,‡)< Ó'iÆ~ÈØG÷u>’wºÒ9×w5‘¬ú¹Lf8ë$„¿Z‹LÅfã4ž.çÒœLVsR÷¼`ÆY ALjg½)ÀÅ“BÏj~oJ“4„³}vZZÉU):èg {·£uü8P¹¨x󘳜ï³leüÊ:o]éùÙìÞÍÄîß<ï{ÔǽY/”?3Çðé5œÝà_µÊÐÓ1)4º:mË¿–šCuŒ›¥»¥ Ì „(b1#»/4ÜÂc-Å;Ä2Ž…Ç£«1T]†ÆcFzÏf¼ñ…YÕ#5FmUSZ6I»+dU½^¶S#/›4ãßO ËD=ô[¡©ÝqŒ1ùÚÕ¶UOÛÖ±Zä~ë3"Y·þ‚c|4oZ–0åÁËú]y‚üQ›‡ÛQâûÿFÃãó뼟#/6#/ùAA“Á©zø`e°¶÷¢—TT•˜,„UQ²–PûzÝkLCh¢%jV•MP-R‰¦˜.ð€èÔdNdƒMÙdJ¿•ù¢ç;­Ži]š\òQ?†8ÁUòØ–†Å#/ yJŸµRN5ûZ+H÷5úZ:||jînÖá¹½h§ÂqÖŽÌìëÙ˜ÕaÚtèI«Ý#¶Û‘‘§&ZƒÄ‹#Œå Ñß÷CÙmÃ)ã.#/„6G®it¬„/½iô®µP-§¥Ž¶›~òâZ0ú:¿~®J+JmÇM}î/sÚ»±¸´^i@ÁÑñ©Ç%”ÈCÊAòÍThÝ°Ñû|Æ‹Ç[4¨wpUüÜϧ‡ýú#稺…f¾\bÊÏLc‰Ñç¯B°Vü8éqæ÷ë²^©À€¡ Æ&[>¼Üµk6ª«Ž˜ñãl<ø9^K#/ÎÌSm/××;)ÂøtýVÖE×,×iýëþÖw-#5õÄ4&á7ÂߺutßÅdwçi#/>÷ÅZÙ~ŸÓ™¡©]w«DÚñ¥ï¨û|œ»}>˜ê®#uŸ¤†òñ´ÎÉáMgYYRóªó«TY¼q‰f¤Üбœ*qoj å›®¥ñ“f+­?Éæg—mº‡Ký¸Ç&Lã·C}W$3Ó«·4àe)„Þ«­býŒÕ“|×Æ©Ú²ÂÐ>u?……#/$î‡Ìã–æÂçŽi¤[Sß¹åÔ‰¦Ì“æƒïù¯|Ó #/‰é®Sw?„ëë›Ù{œùFÃÂ.Ó_Y$ŒøFïl;timâøv¬¨CÛÇO”+)ŽÐ~:*‰ºx­d!žÆ4W'߶Q1üÖðoØ„÷IëúÂfOÏËz6½Š²k.&Ü‚õwsŠð$D981…2â®Í»±í´øåsÊ#¬ŽOn6ÖØ›*3לÊ[LÖhùW’"¹#1˜|α#,TªME‹ºMqéªoävs6¸]–/œqŽ\ø<$öAó©C}¾%è•©LÑÉáSéM$ÔV9Ÿl{§šzk;öO~)tW¦\¹kר›¥§Uo‰˜–¬LÏ|^Ó‘s›¥žsÙe¶»¹ÖÎÝ›ìanÊ‘ üj‘@z•A6‹ßøý¼†ö´3Ž°¬·‚W{x‘a$ý++o}ÝZ¢@þ¨³e]Ò¡ºà•3¹äúT?Ù…S¨ÛU5ºÚaŽãnæ~ØbPI𥠮Ž¿/;#5ä‘ú ¦ÞªR“9ØK8ǣü¼9·Ùѽ(ÃKhœ-¡ºGsÂ(Ùk8Aqj wÇ”„Uü° s¢Ç“ÏŒÚãî‰Uy_¡'·~Â+Ž~ÕXgÔÒ]©I•â#/Lx¢ùBw3Â>gGè•B²BÇq×]!©¨w]ßçÏœ]vÆg›ëNßM4ô¾À§aBú9æsÒ5xöí¨£ÞsµG ’†Y¡Y†µ£$ÔÃîz´”¹@¾ì3š^Gt—+Î%y ݽ,Ù|ÛõR¡T¼$¥¼´IëCî—ÁYÛ;gX²$…MëÄäóÇC ÿ‰§Ù¢ªvgÑdKÍ9oI×V³…™ëÅ]ŸJÌvsȵ¤ŒPRÑ®¶Œa“¿÷3QÙ¬C Œ>©½–Bû÷ìÙ0´·¥òTUBšˆEeƒµ¶([¡(ùhí†G„\ìÙII“Þª‹K®D+²QÃášÀ«YÍÑæf®_m#/žìt};ôx#¡ZÍÎÖ·øÆ>cÿ8K|MPÓÃýPññå’ŒDÂd!,›I¶KƒÊæƒeN–ÇMÑbˆdÜ]‡ç;ê>¢ÉUò!¾yzwîgÓãòç¿ÐªPÛî[Õ@ðú?š@“L$NLjÿU­£¤PX´[7åä³ev4ú®P©iÏ›iyÐI'iq v†Êyº#/z¬¨¤8è‡Zõjòú¾›ž½¦Æ†þ Éîæwñz{&|I¾ÝZáÐX‰˜¼DÛFì Þy‰ÉT,cšÉ˜@#,˜`>únãùªfk­ÚøX~8Â~%·e“¨ócº¨šÝäÝÿ<ÿ¦æÒÛ Uí·i€Û À‰G’oŽ ÿ_;#/¿[´²Da©wà¨Õ  "Å‹>gΤÇÙµ]'•neÂfqU Å‘©z='U.aš6|½¥v•…O…¼¹süÓ¤QîüåÜÉšo$ !ãñU³,ÉU«}Z#W`åÕu”8Ò0HÈE[„^ÛÜ`//«]º#/tP^(ƒ–¸–wQná…·„VÍ%Ît’bƒ(ÚÌÌám˜Ìݽp“L'o˜“ 5û'ìs¥µË/”ô›Œ@ V„ÈM^%€æ-©ÞîbæaµÃÝ—«©bíð‡¢›ú~øûþâ€cþÕ À¬-Á§ø°t‰š„¯£PŽ#51U·Oìï,…P‡Â&£ïöw̯m¸âÇø?èòb®uYƒ©ëµ®Ê9•TñÏ?ŸM,ýxÁª·ögß ÌÚ¡¯F΃›ãӮݷXG÷ÂíÈÑU^ekuþß'TI²A¦Hk+Í~Fî!I—vsõ×^Å€ã€ÎQçs@õM=¸ä˜#/¤fÿ»¦ÙÕQL"žSL+Ü¡KSiÆÔ£ú sOǃ:…NVʹ”SsAßPT%B'ú? ˜u'.BÝÝ®³é©±ËPõŽÐh`¡¯1ŠÕ¾¤´¥æã@vü©õh7)ÝwÎyî*³Þ‘Á­I$5!ÝÂe‘óÌr&šmïC³C(¸³W&HLÁRÊý’iÚB­YàÓ{­ØNlÍ¡3¡†’)G¡5ç4nÛ@Û;Þ7覧þÊÍ‘‚)¾œuËÌÃVfeš5IDðç”jß„!@¿h¤ŠG4hf6D™X9FF)èÄ»ÝünZC†ÄDKßm£IúüyüÕ¶aÓÒå»4ÁAÀŲ¥Âñ?†¥´¦g² ýX<Ä ÂZ3°áÊÞ´IÊc#/ŸHªc?Œ‹œXXÌúéíåv‘ïìÏðŽ[Ù ¼»Œî*뉣êQÖ°ÆŸ{wN4‰Fè·ÀLdIAÛƒ´ˆŽší3¨×[”˜B¿wE fì°²‡Œ„©8÷p€»VCùô ¦§±#5÷L5i8.^£TéiݬòÇ40öþ\Î×s¶©ì\løîlÿ‹=ÄlêŸp·¯vöµ®M#/µ>ÇùòúàW§{ßl½3㢂̶WTô™ÈÀ×xTs+•¸S´ÀÚ{¥EP¹§~n^&¶þÅPï¦Ê.XZ9™IÑ®¿¥=SF™9ˆg!8ïyùþ—W¡4`˜üÔytÅ1î˜×ó©7,»/+i'}‰†nß”Ùðþî©“–Ðû†¤lï°#Ö:§¬>sñèÒòš§˜ÊÛl;BÞ`–§iùŸ•dŒVÁFú8Úq”ªtyG6¿Ÿ5#/+žÆúo¡E{fð÷w »¬ppÍ(”&d¥ïqÓ­|ŸÃf¸•‚áºÒIÀçË¿1œ åU‹c¿v¨Ø餑&`q|kŒLþ 5XQpùàâj‹‡Ü23(!°Èò¦íæËî–;¤„Aë|»¿Âö¢ãY#5mÜJä:Å!Ó·³¡ñ¦øïûkM³çV1»è¢ÊÇ“’&”˜«aÌ/†'Þóžk¦ùòԉ½ž*ŸNu½ÁD½L‚Á3´‰´µèJß?‡Ðg<á@dCDžÃ#5j3xÚX‹TiˆÒnÀ£:2.î\µÆ ¶Öƒi¤šQccyHà,ÇlÑa%Btš“0úæ?œq§¤ôÌü.£C¿ÿ`‘BÄ=uìç"1vŒ•€‹ ´‡Ðb*póÜÕe…6oçü¿ÈÌÌ¡™´!³¡!&[3F a6?>¹6LÕâå\¢•AuØÍ·¬.ÓªúR|ân [#5 hŒ;2ÌJAÈôSÄ£6.²ì³fÂJf”óêZ%•+æ|bôcÊN¶G.ä¢Óg]w$¯Çû93ÆÙéó?³ÄÉî®Ý5×J7*Wk˜²jrï•¡ ªÑÎ{ÈoóÆ;‹ÓXýZš„ww´Ù·¾ ØnÚ3‹óÄ@ûƒâ\ëÇHfÅ]†ZdjÊ!“â¼v¹5n>OsÔ5›_FjÂÿE[Å7sn×(Dð˜¤Úã¿ã©Û²f}‹ˆo(}Ë¥é/o¢ùÝ3MÔ#/*ô\˜ÒÛS‚c"ðµ]tÇe¢°üL(Dvó5èyÓCêÒeòó´Ky£lÎú!ÂÅ8–Ud˜ùî–Ià ‡0¹6ä`‹œ½B˜àB[‰CÕeØ©xìú\ªN¯ŸXÄ?Ÿ~öŒZWÞxßV8gŒâE/ptv6À¨¿1+Í- ÜKu—–™‡ÂÁ!+²5D7­KaA³Ù¦qšd·Žˆ=3`¢QSO›ú»ÓîŒýËñbYuvL 7´Æ` þ_Ïò3”‘ldÚZ¯®Ü+ÔCä–|’ó9©Ï»ÀªóÞùò{ê}2%\z¸m‘oM[ ‡þjÞ=Ì4å¾q0öÁH¦Y²aµ8B󺻼ó4æ¯;‘˜’,i•¦JH,Š£µ¾XÐLèÖ¿o?Gßœ=úr8üžLÔËe%0Š(5aH­RšÄPÁŒ+š>­{±­¢jED¥EAa zêVå —%YbTÂ}~Zâç 9‹×;® Š´< 9ÅFŸ*ŒP¢“’Oö~º+fcÈÇW%Ÿ®ƒ’莚ù¦‚0þ²HXºÐf¸0íf¼d­ø×Êú£Ç<wòÍT¿uH6\‰²E…0i©¥Ìà]äÉ*ê‚ÐÖD\LÄÀ‚l`Mü6W›ôñŶ•u=&0á‡t……"|"n¾½¨Æ¸‡L™×MoãùvÈ=Eóç'8H£¿ÈSÏ·©¢Œ–!#NÌí^ ýó†Z«¬i[<.ž6P*‰#5¥«7eP¹òƒS†¶ÙßÐ7bPžG®æˆŽRÖŸù2TÆ4`œ@×8jLL¦ŒF“K"áfƒµ™­ ‹å™›#5`g©¡œæÊ-TbB¢Â®¸ÜÑß]Mt:›´Ë®ñ^6ú-ËJöh?­‘¬ù¤1Š¢¶à¨%¢ó0ÝáÑ")œ…" G¿J.ëϯé‘úw½yŽüˆSªICQÄ(`a kï5¼UÉ4[5-Q¤aÉ‹#/XEÂV\ !<î­r×ÉnÍ⯄VêVÉ­š–½Q½é­°åBê£hD‡‹F{Ip×kN:µóŠW9HU’غ¯«U¥.àHªo>µ]Vâ"/z(•~©r#5°«+j¾O AÜÀÎÆîK]›ÈcÙ«×ûj¯v={[vëƒdIÑàj=qÞFRUxoÍ¥Ÿñp UswØU+nnÈ:.öÐÍôz¡êÓÓ–e~ƒ<P¶1öÊ1gMÛ7*â_'‚Ï0úº½ÀJÅ7(’Ù#5™H0Q’車£WË2¬úr«û³Õ“u˜­¨”»ŒªÜ¹hÂÊ) OƒnšÜt¾²I [º_Ž™Ù¿]A8âòVT‡ãP[úÜÔ“™NËù¿Å›¿»qž|tKƒê¹Î[ãðŸyVàWí­÷Íø6††Ná¡—´MŠ<·u,Ư âLÁ³:FßU¦hÞ‡1ü;HÆ‘Œ @ dJ•¦ÛÏ/5Ûòë±±^¸j-ñ/F‚d‹"ÂÈÐ ’ RE—‹¢¢²˜H4›I#/ I³.Îaϳš=u:Äj¢úc³ùô­5¾VR…k/’IÇú¬îvýÞ¼ñÇsÆc«üË©’è#ÒsD»OGÍøM‹F)ÅâH„cpÎ"MUöo¿WáÈu¡uG¾#–œ)˜{žSªd>?«ðr¨dÇr6&foÙ²j[!¬×J­!bSB»%ÖMk6{fþïp#/¶2ѲÞgZwéÊ/ê80n\„9PÁ$ï=®˜Xu,[À&ŽÅŠ­iêŸ ý§/ôËRdOʪ)È(dµûÓ.Z¤ØÆ(Q…E·T(0QµÔϵ’–L·:+wÚŒ‘gwÕ9¼†!ÇJ¯éiKËÈéÛ¥þ²ÙøÔÔs“Äf¨î¼ÜûN~¾\¤Èn€˜6ó$”Á<>)Î.±X‘! ö£&}÷·¿búŽŽ ð’þ2 ×z’lŽëì² ¬v×LT¢‰d$ŒyD}^ò—¶ßgöÎøNÑŒRèuÆ–8ÖT¤ˆ¡Ý®q/òÄ}–þ/)$·´Ø^sfõlÀ‡÷*ŒÛräÊ´‘¢Ì‹ÝU,^;æ\ÓæMt0se#5kã,6EUPq'=q#Ÿ^ºá§D>±à¡lÎF•¾)NB„xSqF†£ü* Y‚$/#/2·™&½¡¿ÙóŸòàû Àܲda=®ÄÝ­,ÕÂ\&ãf*ÑõÈZëÚjô¾½!ÊóŒ×;¤1î9TÆuCBs U.¢¢$ÚŒrÛ\ÚHµÍ¹­ÜÚÝ-¦iÌ÷n½öºDSU<*ì¨.;ð`X&Écñâ¤àèŽfÖ¡“ ¸n‰d©LNk”Ëë8ÇHQ¨rö>ÓÚzÂeâªhS30gÐcÕó`L#š~m!ô·#Íí#Ç/¿ÊFÝTŸoÇÝï]ýltËQ~×PÌbæiŽ«ê±­ #/„¬Q÷þõm¨ã¤ÿaëÑÕÚ-;ݾ´ÇêÞíÒŒ[ÓC%ߊãÊÉIŠ¼ÔÝåå«çÖjÖ<êyuåB?j§«`Áœ?Á½ÆhW,¿ëh¯ÖŽ2Îÿ>rã#íåoû«"å'RúÎôï ïPxÅ—²W–Ç+Û“Ë©­iÈ"#]“¼ 1û³BF]ÍãÈŒ²Ä½4†Ÿ=9üxOÏsÂzú_ìû;áDUãâÓ}><ûáË/1¤ÊÞ2n­ÿG&iŸ×LCk¸bÔƒ¨Òuüß«/5µÓÖ֓*“Ì:rëìÙ•·¯¡_ÑÄñ^+Ëùëûª•Òó]ÐZ!ê>Ì£Ù*6mçõ¿ðÍôcÙð>N¾+ú÷~Þ>ï·òųæ3öf_ž:Í´™Ø½gDøK;¹#5ì¡ ×‰E¦Qx Õ4{hyOçfßñì²Eʼnb|•ãŸ®_'¯AC9<Õ9³zt¢“FB”Ó–¨P!õ×­¿õL²k;±ù×Óöwé·•¶ ¾Ü#5ÇiéöY÷ÂŽÁ5‰V¡GW3à«Éa1¦”4Ü åÞQcziÉIP%4QhÕ¨%B¾ïßF¿Ä*†ÚhÿÒªB-ôÁä‡IWïü¿Nš¹™™‚–X0; ]ËÂâùsWŠùÿ{wëßÈsœ¡™ÄÜ"Ä[ø¶‰A™!!…Bb3âv´Lµ%“øÊæLHfÊ–%Iùf&’Ò£kúÿŽvÝlQY\Üvq0Ι ’ Þ&¹1 ¬=Å°ÝÖ^[-ŸÌÄ? MÃnzM¦v¶‰e4.Z)M!t¿}^Mzߣϻæù1ãDF"whª #52’ØO€b¢‘S†‚&H'{÷_ôLô®ÖxŽA‡N+Qó¡Ù ÈdD>žŸ{ý]L‡v0ŽQ£ˆ0›$3~ër½¿¿®¶¿B½EXŒ°ÚŒx¾gáb™d¿é«Hu¢„dY¼#5gEkò«Öõ·K^Í]J½ImóÑ\ˆ%%„Qñ.?äùÜúS¡Vª%´ÉlÞ"–eHhÀ¦#5[*‚‰àù]Ã11þCHOÑŸ0xjgüø/¦$(—ãoð³¿%Œ_ù#5»U0}q–_ùé§ï^™?3üììÙ:¦Ú [4À6?¸ ž³S»Lë[ž13£ŒN´?á½$y×%’1@z‰»qÁ„ ÓÎö ¥mú"Õz_ ΢׸n"=°áxM *¨èÎ3 뚣IU+«ÏN_F,Ýø#âI@ÿ·*"Ãû.N§#5è’‘P‰CdìQŒ.ð±q.CÛÒçúBkÝ…gâÖbkßùØdE=l|öDU)I÷0¤ý JÃõ´#·Mj°šëjú†Þâ–füÝuý‚å£W¨ê·/ÆÅ$㇮¥‰ƒCá?‹òÕÓåÛë>Tbß-ß»Ëñ§®¿¹Ú¸sÇž>8[Â%hæïÏïŸyù*ýÿMý"MÅy—JûŸNm…>‰Ö¿*™Ë匩E¬ÄÆi|ÿXçËgáÍa×vö1•t‡=¦ŽÏßH‡:ƒo­–ƒ˜–Rô<5áî{-ºÚG"j  Q‚¯•þ³&jçû(áÐ*ú°»#,ÀJo¹|ezmûf~áàµÅê¾?Ãþœ)í†ÌýúÓõé ¬»”M¹õÂ''ßÝV7q™°¹,RveS»wð28É/œVÄídîð$/@T\cLï¡îª´3cv¾»-E±Ø£L>ÝÀ4#,Ô%ñ3#5Oóô=Q[MW9ÙetŽ˜Æ=cñõ_[*[åØ´å§ÍEÍœÕÁ²Ï¥¯è—zµd ¦ã¯‘a¤ÃB#5LÜý445è>K·ñW*–¤ßYÜz¡=„¥ÆRLÜíã?Äܲ\*™wÂyÿ~ïÂ|ŒÎ¬ÜN& ^Û ?.¹©ÝT8#/ZÅ5ý­{ò€¤êÍÄ…¤c‘°o0€ÛNÂsüþ ›:Òß9t û „"uúO|ÈU¼&;éì·ïl}ðèÞIÈ~³ù¡Þš“°G-²DǯŠ‘,îìïå ßÜñ‹ýoÒÈo½Îc|ÈÑÙHxžG¤¯ÔDy¤!&B1d8ÐÁüóù Wð_¶?uŽÙ“fñ!’*„ùO@‰Ö˜|‡+N/•©ù?ºuôgYèÏn¥›y^9ÌœûS:gwi‡³ƒóDáIM~‰ˆÓ4ÑŠ »LFÚŸµè{!V4 A2IbçBÉpVl­à#ƒýYà.½Üât’‡cšH’LJ·×~µ ¦ÿüÁØòû»Õz0:* #5|ÀK‰¥TR#,Ý TDDEWåîñ† ÝÔîöLoÏ—M‰þeaüµúå”Ò%'ô©_ããÖ\Zñn2Sh2PÉ,ƒ ÎAÀ1¥ €ÒŒ]îoFƒåÖi31-´t¡1ûzŠÉøÓÔ¿ŸÍ³fîùfïû{{¸×êòâÇÛê>th|´tÜ»XöÝg»KzÿuÞÝGÈqü·Þsé#/xT{sÝ ìÀÛ3Gš¹ÆšðÆ/çPïêî?^½tiÇöíʼrÕÕùgÝQŸ£uÖBáUüèòá»~Tr«Žò¿úOœ¦~—½CÈ·x¹éþ8÷OMµ­³ –¼wkrùãWÔó‡DÍøÆ_)À§ÃuŸ^ýÞxú—ͧwÞDá¶ÍÝÍŸ<÷Ó»ö–†ˆözsËŸ²Û=méòñ5™°;²õz4ž\ú5žž^-mÓø7¯ùü~}7VGãi²f¶ÆœףɻÅ#/íæ×+%BÜ{bÚqóöž9N+ôÐŽ:àÞ‚¹í,9Ç?§fãÃg=÷í|ÝóÏú©–ª³Å»«–È×#/¯J¯ÌGFÎjàu~~=vû²l?¥0†zJäOtªå§Vy×l«4S\‘,‚ñZºö´Ipç=%Ò>^û.ðSž]Z·U³´UÒ:k²;9ÿ]1‚öâÜÕߤƎ|vø¡nj·gmJÊy çÝ—IÍ®Ìô°ù\ÀÆ2+ׯ‰#5U¨<8œ„äŠãô<¡÷üpm~òÝ:ê~Eüæë|°¸Ù·Ô´éJÝCø‘?wŲîû´úyüv–CŒ2ݳD'år.gmQ+õao.]¬ãÍeû=WUZeB+ÏÒ¦ÞZ¡#5•kôòvPÑþØÜ©ºþQÉ®S†Ùuü¾&¶7Ó3û¿ >¡´Iëæìüg™ïª’ѽSIŒ‹ÍR¯ß$£¹ÿMx~O6{‹!׿?ÎyÈß- ®+òÃdž_vÛ:¬È'ªÿò=Ê¢ï€AáPe Êß^»óÚlËuîŽßoš>ä¿n«—×áåáJbíÑüÌšÚÛ£þ÷×ðÚÕŸ»gËüý¸òÕ›B‘\çýœz*Ñ‘õzÆ¿W51ø/U[dXhÆݹ»gWó=™9µYgº€^ÞY÷}’¸³O]Ó/n¸{kÓÏÏüvëøº+¶ÉæMrôádÜ„ÿZK×\6¹þ8éjË“qpGtK³”×°Ž3öfÙ¶a²È—ú|ÿÎË~læmÙºOy·ý¿ËwÃäïúáXo?[ïæÛ|ñÏÓMB-Ñ^Ÿ]>&æª>ÚDò¼Ä(ŽøhðúwsþZ¿IºPØòÅè¢FFU!dÔú9Þå—ÒS“åáqC°H¤"œ‹L$⟎Ï_Å *l‰¦>@E ÃZ˜¶25˜fÚG˜—Ùòi÷ž ß»´«Ÿ´éê×óùù_ËÁ—¯òù~z÷öè#ð9‡œèòô°spšìúX¿oë­¼ñì;Ïô÷ño¯òþãfoš­Ñlí¯WEÞê›CTÚ|ú_Ó~˳s+\û~_x~;tÆ~£Iaæ&Ü^–¾7Í«/=f8|ÛEW.¾vÕ®ÉðÒ|ìº'ëÓi¿ðêÌS|²·•Rzì¹Û»låøرuû+êZtš§­­Pg•±JüíP9¡[ÈVË1qB T/;WRØž•¼/“Ý©tåÇTë²™^—Ç®œÞîeJKNWõf»–TäSsv'k»òÉ)Ês³—Ù Q¸ÂÜÍÍ)ÙýÜcÆy#E’I'!i!¹Ž¢Zƒ9/ÜÛ?#/^k#5óíõ ²¾>­_˜Ãâáâê¥;žÀÝ*kMê”;þ‘:ª'¶S©¨f‰Å<)ñí„>8Âœrìçß„1ds…óÂhœ!rÐ˦~߬rl}÷5ŠÕ½¹µwTb©<èäPÈØíÁõvDN¢Ô½ãvïŠ~¾UÞ±Þ·!žZ‡ŒüÒPéZ\ZgW ËDK¢’áBQx 3d¸YfHg¿ˆ—äöYp¸hâ×a-³Òf*•ùËrϯ˜ªÉ™l¶üÓ›™½[;âh÷üòÕauÀ‹bq&„ÑÆ%£–É„zV€Ë1 V­„±}2+†ó I–ºW k-ìÞ{ÿfë„'¦o›}†üµ=uÛ9c{bgîצ^cf·×mv·8›ôBP”+çšP[ÂiÃËMùõ>(c“9§HJíYŠ¿ÙàW§=‡ùK¼`¾—üÔ²y»5íÝÍò|•V¤r]:’®—vw×Ê—Ùëëü§GÃ/¨Ú#“tð5/ÓêÙ#/p<ël{ƒîŠ²Iÿ_/d¾Œºnå‹Ó¶žÉF Ùy!ÝÓ4"«ÎÞ6Ý}F8#/ÛôÇÕÓîóú¨cê4™G¼26©tÈÔsyJ\³Ó×Q`š„ùd`Žï>p#ûIȃU^^J¾Š¾©…×ê­õ.EwË»/¢ÜÕøý$¾ŸËWõ­a\q´ZŒÌ¹‘²¶ä%`Ïž*;GªAºÖÇuöÃta­¬¤°ŽƒšVŠ•$Ç´Cc†E„‚ª(8Ü#5*"ªjDÚF 6ΞšÛ[k[‰Ò `ÒŽ ÈLXµ‚V“a¦HhÄYSI¤ÝU X–š’¤0­Ò#@ÝÓ¶–k¨¨,›Ä06…ûóüY‹Ã8h Ô$m÷E¡=ø§M§X¾îóÈÞ³!¹À‚ŸìuòX~Õ1©&~˜­Zj]'åþáhkQ˜XÁàñغ9ˆˆ¢13S²%”R(Èùªæº t{¢t/9h#08Ž$ø™”ˆŽJ:QFk1}KIPc 0i`QV‹† M@„4í Dé Aà<Èwoì‡fIˆ”xS-ÇÔgoþl,î)~½ÿŸt»gÒ·Ÿ1‡ôëÂËóQ¨ñ/4W¥hÑÙÓ?¾«9¶Y¤æül¦Zµ%û½CÌ·þŸÉæÍ÷a_#/úz~Ó®ŒDÕ‹u~½-2üÔß]Ÿ%Ë<ãÙâ{n¹w;åmxû'š”ÓæýóÌ=ÇÏiäÐ1¯¥¬i5B·”}#5>fÅ®>ãÉÕêý_ƒÿ…^­ZýišöBB°ãÊ DÒ8%k½ÿw÷­?AfoºwqáÓ×¢}Y?¦ú‘³ñ…Ñþ’iIå)B1x×^@Àäêñã?åÛáO%>ÿ?㪥Íá4N=ßÜùÏ/ÉguÉ~>w祑ùkêøºÿºþ®þš»ïüìݸÝû«ü5zë(¨–‡î¤à}†spçGÁÊ|¢®õ?È~Aˆøo{Í8VšÏHm0ÿaÌcb!…hô22Ã!I¥n0KJÑÊØ¥ß%IH Ó@ÓHÁ˜2¬)#,i³ÜuF Ó¦$©Z¹LÚ›&ÈÉ®Ýôó^šö÷ÀÄŽ˜–™SÍ)jŠ€ÄÀr7h(UX¥¡`È£¡ÚHŒ*V&šefeX6<+-iNþüF„<Ç+Ú“{†º†Í„f#/² £IºàEƒ©‰‘AÆÚN…iÑÅv“yåN=Q˜R2ŠŠ9‰Á£DI°n€ÄB° ›pcK J ¸‘Š)"Éó†%ºæ¡¶„¦ˆ2)@lƃ2#/e&:EÕ‘'(<)‘LRÛŠX›-7օ̤«J#5%F#50¬ (älRÍ>7³Mi¡²ˆ‚Æ—s6„B§ˆ‘îÀÇ⢷f“ /öZU™¨¨V¦œ®Â jPï‚ÀÇX`- ed ?@¡ÐóÈQ§Â'Ѹ˜.j.DÝÊGºìqrº¡hs#/ü¸1”>=j0}'t’Pƒ»¼0ïsòM¬8…Ù#/¤$Ò¾«š0,ô.e‹/³ùÕå¹óš~hsÌÀ7ïQÉá8m5_:ˆñô{þdã·ltþTmg"éø•vS“Rìi— ²v÷I¢n7]#/CŒŠThb¬ƒÖ0£«ÊI¯DðZª^1¢ìfe!™i?Ï#8þ^[©50î!Ý5ô;ÖÿŒýÓôø›£<(.™£ÉÄ#äv;³)™ ·¢€¸ÄÐ|oà¨2{»ÙËþ\þûn¼XôÆ–TµMå Ú­EUµ6Ç?ðíS§Qá×±ÿfhèïÜTU¼Â[wBëµÛ¤/ÑÜž¼âBBåWÙ&Ž/^ša6¼yõ›¶MšºR—±FåºjnþHÉ$·o½•«hqØÏ#/<Ú”%­èµµ£&¯­¢r¶ý;_É56„UòȇÐVç1Ê'}˜}CŽnLé €ø •DÛÿ®±¦Ú¤ld„q‘!¨—ðb¶?Ó—MìÒ60¬Î8;"Ê·LÐ!ÅO7³éÕôztÝów—ÎõøOþ¿¦_DÉ‹cꀠˆTH(¤QŒèkš°ôÉ(VôMÁëLê ]Æ‚^©†1‘¡¥aÃ[ÌÃTu¢šùè] ÒÞà†r‚Ì2Æ¢Iæ¥f˜äq|(£x8V‘Šã3ƒˆ]»” Ò*0be¨°#5¾§¡è‘MÊx›X0¯g½«Èâ­–êŽÞÚÁgáó1@¼aå°Ù²=°¬h´;_ÞFC4FµÝ£*%ié†'äeCÑK9ntlV ‰ž™’Æ‘à:iëC9ª*‡·§iâ3‹b4‚ƒPK³5¾j&JQ@z+F ,#Q6ʵ´kï`bäJ.×^Î )iÄêvº‘rŒ‰6Q¨k0wçc+z;EA³¾hk ·ÄZ4”!÷îõ5M)Ž4ܦˆæšÑ ¦ŸüŸéž&ÿï?}éçʪ½Ñú;úéC£êÊÈô÷S^_kwž3^c¿ÏýÞrìº*>äV¿¿ˆü)úØmâËÎI ‘tÒHÜØêluÚ~&|f7¡TΘ77lÓ€ãDc#5çDŒx»S &´21· 1›l£'bF–åÖ“qÄB2š‹õ’,Ter¢Ö9q¼UºöT²ÀÐœj¸¹¦²dÄ…”9¨´SLŽÄËÌ…’™A ZaϬ|C;öÒ®½-j†‹´ªÁôÞŒ¥¤e]Jß1§×@l*œâCr¦[‚Çã9¼P@™è… àtДC0]#áŒfØûÓ³AqC­I*V÷Á“&sbÓ-Œ^,\ņÊy¹`tœZiAȤ ‚ŠGb“vHƈé”OR¿ºkYþkÓ¿©wóõä½üÅïŸïTGò†ÿgÇ«Ûë{ç1J6OøÅ)ÕãWj¶63sr²Ž„Z™ÓÀ˜0É©4NX°#I(9NÆÉ‹³»ÌlßGqªVdþ¸»­ÚcºLåpr¦v‰ˆa”G›Q#5šÐ&àð¼Ó‹0œ‹Ô„¿L¤—ô#fÌýí£#/êüj̳˜ ë;d3ÄÐãž,X« n¹bŒI”ÈòQ”pÔ.6ó²ÇâA±£a©¼l±·#f*U¼Ã2ÊeÃs]×›ËÍçW]9wP$$¡°`ÁÝšÙbЧ…ÂÍV¾þMw+^ïæÂ" 7¾+š±]š](Ò†‘ÔÀ¤Š¥R —mdÓ×GóÕ¤ÆúÿtD˜(4 x8)#5Ó°ã —È’4û5ÄlËŒH™˜¡#/§ao|›!ŽñBiNb™Ì!8˜‘±—#/’ÎoàDJÃö°Þ[áË€™%À‰Uº;åœ3˜aŒ/Í8kÒ fA#/¨í;æ~doa™]7›8:›ÙÉ°Ê`”ÂBLìívP¾|l¬kåóñx ¶Ð“ZôãÍ©u4„¯fù}’Ê]¤6ªºÉ²¡°,™&M+ˆwònÁµÛ#/à ƒˆ8,·äF Á‚÷r7Q¸J-âœÎ-“vmk­tQ6v5#5',ˆÇjŽÚÔÃv+zâ΃®©à+GÉcvs¦ÁÒ´ÝXÎÐqšW/€š!³X:XÆ1–¨qïìºÍfÙÂn¹ª\†¥HÕÐkƒ… µ‘Æ»\ëÙœL‹O¹®psnÝÔç\“..L“Òª™Zµ–ç%'frf“€ODHåaô;ôì÷Ž#/bûõL^…vîóØ\“#/ç<ÓLEç¥Æ#ÀŠ5x^éö'>7®±å->6ÚW¤òßoÌùëµôSƒ-¹ò~ÂÄoƒFqRæ2>De^ÌF/K©± ·}`²ÿÔsD!ô!à˜ÎÉŠÄç£ÿÉ&û|¾=mêw%²êOçÀõ¹£~k¿ †§µæesQ»?"ë0òJ»ˆØ+Uny\(·˜›qUF̳ÁSl¨·ï©ÆËês¶Œ]$„¥:dsu§‡å%ýš÷̧_#5sûL†3Ï‹{7Ó›fïÇ|2SDï錙49õÐèõi[ã>•&™0™2@×ÄrC’™wì®ï·ñÞfŸË™ŠÖYmsD¨ìz;A‡ËÃÍõ:vYÁ—Ášˆ˜1bð°]2ÔøÈCæDv!ÌRoñèqb|¥)iÅpg“¾«ŒÓ'šZlÿ¯GL·Û#/{Oíq„g=ùo¥‹X[èÃ¥¬@c«‡‹„CY·2Kdu£é}ÞˆêýÕk·;9Œ5[þ½§\A|+/éwÇè£b $Ú-2 exìÁ2f*Ÿ)ŽÑ—-üqÈØ2ô’½²\3°bB„b`•E£ú¦àoêf/\dÄ#/$]‘ ÉPq©A;HÁˆ¸;€Å3i»ø˜ô3žÏµ®³¾@˜)à‰DËùÛœsª¡àLï”þ¦íTãÑ¢¯´Ò©H©‘¤Jœ“ð&á=Så^;Üy¹…3«€’ƒÔž…K¿‘Ó#/ù²ôa¼#¶ßJ(¿¤ð©'ŽôZen· Zoí¢£Ü²[¹ƒ^o(5úÅd÷o]§h=Oh’G·&{¥ü#5dýZ…ðúgHÑzëºìCZ$V¢ð¼fS%¦Ã{i› n˧ª(“ ½‚0GË«ãšßÀ™)'¿|YÀôxÇhv¤™‚!Ƽ1ç׌ìÏsFÍ[HÜÑý½œuh劥ûÅÄxŽZ_)éúD©iœ¦jL<¹ ÖyÌc†Ì¯E¦²OóON4Z”ãæ&NFÛèÀ»C¹ÀeÑË5Üß$?ääS¯?ÜD3ÊŽ¦ÿN6ø¯³ v‡«.å:¼èÆ!€Hßã<ˆ]27|jƒ$Ó‘î¨0ÜÎTé$=Ì“,aÇgG>ãôDÞ‰á@'û.'Øøñ£_%æš·‡ ýUgn=-šåMŠroš^bZk£œÓI«&˜t:pé²2¬-{n»}nû10ÙÕcb(€–æº_FÕ±í£+ÅV›-÷´[}ʲ³PíŠÌ©¥ λ‰¡ÜëÂG‘Üv¨¨3^Í1'bøŸ˜ö/M˜ùöÑ„níevè›;ôJª®åÖ)l¹Ê—qµ‹w–‘¸Ìv]έ–j–f»ãÄ;ÿåQ_'.=”Ó‹÷¹”+Ãçå³ßá~ÃÝ‘œj°Òaß‹ˆg—Œ÷ÊXÆßXl´#5¬ya(\CÒv»±Å¿w¶í3Þqf2N"^JHç—æÊØ!þûÖ‡ø¬¦ìîË‚ÞSØà‰¹'*…Î {æç\•¥Ùq#/3”Ó†_gÀ’P—]ê<¸˜þ_Ê篵äúÞø¯Yï§:pùÁ"&¯-P&Ð5*¦÷¨Z¦–¼øL‰Ê™¶á™€ÚET4 Ë©:G¶ºXØ_sBÆ;2וZ~gÑqwF¸GFãÎ"[1”jjáuIý&™çà®V÷Ú›zC¿‡lg* ®¤]ƒ Û]jP‘Pí-Ç~Â#/Sa£â›[ëf<òÒ‹àrª9–b’ÐÐvÍzg¦­°¯\ë|õéQ’ h!ý^MÜmûcÎËÛ¸‹.Hn%ÓV3û—¬O¤ñNc3ßi{=Ûæ¿ÉÕ¬{ÙÛ|Á6ÓR„ÐâZôoÍcž.±ìÎäi.Ù(Ûa–U%ÎE;Å·¾aÐQŸ&Ò-FL4aØW*µ$MÚVÀƒ ¦!ÉæIˆ9Gתªúã§bDÝdÀ:Þ¤¥-WjjWÈè^6ôìUðäéÝ;§·KWñ×UvQ¬·.¡„*àóJ#,Ü‘$†s\’8¨±Ê'ø {ǧéÌ¿=sœ.ƒø/奊8<³W”¾=¦Z8l`ï;1)qŸZ·zΨœ{¹ÕQƒÂ‘ÌN'â{#,ï“ß®FâÄ›nØÑhùÇ;mñ1Mé²÷#œËŽ™q(¸VU qCáêÂ%I<ŽÎÍ™4s¶äH“°‡¿œË51€–$1¾È'lLq½-wqVaé¢4í&n½2/½¸œù§Go\ïµ\²t“÷—%øó¶ ìÖÉù*£ß2Š‡ðÜ÷ÎL$mð,¨‚<#5ëÆS2Ê[Áïs}ÌË¡¾Ïûï¬ Añ×R&9ðrD·,Ö(ƒKÉ賨Ê!L½FªFv³¦Â²P!f¾òynÌi&•îëaÁF,Î=x)“Re¦Þ¹=5"^#?#eóO•Ç-½eÀQ€~²,Äx-¸Lª ôÈlgW‡ÕHþÃ×—ÎÌ£}Š°˜ÜˆI n±hš{a¢ÐŒ¹ØšTéyV驵ú0ÞåèúÂÃýaãþvD^fÝßü~îMQN„ÉAÝWG~ïÕóÎÝŽÍQæ3]Ÿ)û#/»lÒîô\ñ|Þÿ•ùöH—ç.Øõ×f|`éøkÌhd7ˆ}g­gXVÖX%AE¯ÊŽÎV≟ £}ï õ/A ±ˆFõsÅÕY~’‰aG6âq¬¶©ªð;ŽÝJ*8…ê¦9íûüBºÊ—7o¿zí<œ}*ÏÉ´Q¦m¢ŽqI«ò8í8ZTD„¿ËúÁ¶[CNÒVF·¥üÅܳk%E^VÏ4Ä[w \3:“ñ¦jüRk)rP௼m9©¢Ûáe7ßÆ«ÌKpWW>ê]4÷‚ÂvÝ2YósÆuJ*rºO$¯œpí´Àž®zµÞÿ¯ñÝðßoÂý¸³°±ÛƼv'®’ŒfÊ”œ¾#ñ·H¾™ýUèi_Âëì}ØÅH¹÷D²ÌøÆšêãc1óI=ÝR¿™ùƒYSeY8=ùóXlY$®­]œ|•t”xJÍeÄ´N ÌŽ<Äckº\y!ý³ú<’2×–°ÓTNzw“^ÙPRiˆK»?…¡79ƒÞÏó{kõYX~›Æêãºmª³á'ámYªÂ™ðŒo€ˆ‘ "í¿ì®…e5,¶·½[—ÊP±µyo,­ž/ m±ë4ßH"$!LÚ&tŠWsW•èf5ð5fH‰l*;Ú®LOt3dcu r®‚#5a ô‰I<%Ç(G¦Uöø¶êªÚê.x—‰ô®Ô’—Òqðƒûf=b¹oG*0'ÍŒÄe{ÃË,bDŠDîÝÚ) ¼òB=‹¾Qe…£ÃG9l5až¼üÕ«¼s*+ó§¤ÊËçÎ̉{æàöþJâîÁìEiˆ‰díÊ£²Ço c[¥¾ —÷>ˆ6h_TvtÞ¿/>JÚ1†û"•Už5(÷ónšÜ2â*KCz×_[¡bú–QA]#èºfqiׇžUyNOS©çžq• ˆ7¹¡u×ø'Ò#5Ñy$ü rdòç53„å®äûÁÞúv{’ÜÉ-q¾1©4L“tpºo¢ß6YÍÏu¿"˜Ë2óWçØΪ9½VBâ§cÆ[Ù#5U;elóÌaWê[Sk\òVôËd.¯‡§§ÏaÌn5Ä“ß$‹Åó5j‡wǺ¬&6ßKxª®kk7q×î…ƒÓ¶d"õäö¬`|¯H²E°§ˆ©3êäÎ÷=k}ZǾéàŒ3÷NµŒóóßïX^XŽžÑ\Ü$Ξ‡Hi%ÙF裣ü|«<ãD U1Ÿœ{uÎ?o!¸Õü7t¯+Ô¿hÜôñkÓq3Wùbbüó-Æêxš…D:hvUƒÄ:s¿ãÄ÷?jZQ¸ŸG=y{ðîºâºã8­òúÏo~$èF øó'·7à 4³Ç ßWÇžkÏni’´9ùêÿ5Á£#5ªÆC¯5ò9çm±¡h3?¥lMAòÄصÜ`[U5¾<Ãœ¹[Unü±>Ü3bÑg\#uNI{®ýü秢Ïw>ÅñÛÓëY7ÎàNƒ‹•ënåçu6Lþ55Ê…vYª nª¨GF±®9Ú©¢Õ†’)¡¢.žÍ<9秊ãZ”YîôyBÇÙã™yj¬µÅrOíÎpe{#ç³êùé°ž•b\ãàA<`STO­ùžÙJ÷YŒL.g´šƒBV•Ae®ÜU‰®¸bU2²Õno’Š¯]`ªD{ÜTõ‹©wjuûD…4ð<梺âcŸ.ç”´}O>k¾úJb~÷n9ðFI럿›ŸOÎ#YÇB*4/9ž2ÿWö^“¬my$œýL{7½D°Œ.É¢98‘}ÑÇ—Æú#à¯:HóÑû®™Jk‡a›”#/û¥ÀòÍó‚¡/wéñã»]3¨mWQUpÕͦý÷™d”¢ÚE¨Ó^£sñ¤Ÿš¼ìÇÃzš0EüþŸ-ü+÷aöï­M‘kÕÝ¢›!Jë7YOß+O++^´A— ¬Úõ& SØžQ#/Ô2ÑG¯#5BAÓhêwlÍ*ìN§n-z×!å>+­5S¨§]S5Oš¶À°Õt#5ÚîTt>ڸϺSŽP×…ÇÚêz>7ñŸ4î·©ž"úxí0µÝ¯æ çzºÐXnß9~]ëVƒîÓro.˜iÇG¥\\\¨$y«…ȼÒAðwÉ2Š‡ TÜ‹´ N“çW4f²F¼Ù¿WÛéJÉå–Ëéﺙaf¯ñZF–Å zº±™$?CÀÍYײÖÌeÅùw$ ¾|µ<.‰_£æa«ŸÂ¹ãXGãF¾dªàx!ç+xóÔë#5B:&adΓ RÞª« hR#5DMüíÚhDc7,O=P®Ã1¿e0¥Ó|›e’ɇUlêÞžøü"¸í^UàPÔ›¿z¸nŒŸ¿=c&÷Ë—ÅM%çß”;º>^X­Ðß4}ÕÅsIñÚ¹¦¼§G{Õ"B×Åå¶o.ZµcJÅš&±š·Bd[× }άù!#5¬¯”%›šPËÍVŠo¡çòWA-«\c#,¶½1ˆD^æü¡§½‹ë¢Šny–„z_„NÛn/¹vÕÔtÞåÏw~ørÎkš$Îcy»ãªcüoÍ`]]¾~.÷ᄦ¯Å–2±†£äãÔøO­›ÄÕ×oÄ£52©Ãö¿âøò¦9ß#/ùÁ€KôŸ·ßÇÕ%½æyÜŸZ×Ãàð<â’¾ªeˆšƒB;s†ò‰n¼i[%§uÕ‘Ððœu¯DzÙÔÔ D]S1áËà ë +FËa  àìÖ#Ç1ú騃4uá2#/óÍôÍw¸@kS.‹s²Ïžwµ¥W‘Ct­p/Yk²Í—xGb;¬;«Ž.;EÇ´wΡ¦;a”'/[Ê© µÝÜ·Bvð«Ç‘(cÛ|7㟨]õæ¦bÖ‰C§d¦™YTc<7WÓ‹éÙUw=Zë0¼ÐǶþþknDqqZ7ÆPlÅR¨jsÕÃTõÕ¿Iï,ù³e šäÆÄ5ª1f÷nM<šÌ¢YdÍèIŒÂŠ³u˜žµ[\­gW…nïƒóV$QÍî>Õ¥÷ž¼TÕÉbåL/éˆb0›é׈֟ŽO³á÷]Mzà™î©²¼nKÒ¿Ôý»ôéÓªéËäܼ äy†—ÃÓW‡ˆAüFŸ.#5{ç®/~ä »ÈúÉmqoG:7—Rì<áá:ߣ2µcÚAuÒ^ºNš°!§CÚŠ\Œ³Ä€ C EÂ|HY9(îKÊ¿%ç#/'ýF/;Ìúj>`Ë_d¥º¨G’2/®^•Ûó‚úÄÈáÞ3—ò›=‘úðóÛÍŽÚ®üëÉtåìùl5J»ÎúúöÉãP?ßïHK×Øá×3¥œO2³ÝY<…òáT LâÊcÙø¡þï1. ËáѤü#/¿kUò„ž—š’ÞAøÄ ÑÈ’ÀÕG½ÉÖšèdÌ·éÅš–»âá3 ø£j±ˆ-1†7ÄŠÑh†K•BÚÕåqy§®ÆOG|`xO Qb(D£ª´\ÿd,¦ò XˆWõÔliï½2뉷íOØ•írqIÍzÎWÖ»,©õãD´Žö‡V·Å¶ûzþ³¦¦ÊÄoƒ]¹9ZbÔNŽoqÚäÂQç·l‘¨¹Ñ‘;¦Ó}óëUC‡Ôܧä -:úÕcó‰TsV씣§C¢Ñøm…dî«„œGlç*:¸IÊyH~Ž|,ðŠé†=Ð?‡n,OŠˆGv‚È–• vHø=Þ’1¾x3!>*Ý»L¡¾¡«µÞl™72éR šÁo¦(§LbåÖÿ5~CµâNá·w©{Eöº.“ƒH^:ì;Š™Žæ’tˆB—zéš³Þäç<&ÉTcKÚ#/vÙGÜPf¯e¿®f|ßÄzm$Ð爄rÿ{Žpr>ËISÀyYCdÅ5)œcÑ‹Æ<ÇxdÖ›Œæíû‘qˆ[¸)’RÔ'¢çË1È?¬·2¨é¬r”T·göl8ÈǤm›É0Ï–<ùËìf&&qÐà˜Ì¯…6e„œ34ëÑ7m+Ó‡ËrRÁ ï³}Pë.dž?g@ºÍ^,嫶cŒŠ:M:ÿ¦ëîÛÕ#,pöÿin-camvÙÒ™¦›ËŽ!hèÅJ¼3m׆Š„¿oŽñ~³›oA1 [Fy]}Œ¹gÒ1ÙS®Šïô;¤ÜI“vÑÂQßæƆ–ú#/»Ôü~¥î5Ù„°ƒÀ»TDJv®Þ_,pcÛê~Nö©ŸÚŽt×ô"ww__§n‚4N-uF:µWø½Úâñ½|~gû†÷bkKÀ#³¹ÐC»–¢— ât£ùÿJàú”äpðx})–) A.ç¢È„t3ïQ¨ÔðP„ó©uÔô,óÂ~;[Mc5)hº,ý§ñoš«nŸÌI|\!ë#h¹,ý§Ç¨V47£Á‹èõíôFãÎ<ý›Q¾9êNs_»)Áëìlîs+[-Ö¼¼3×li2ÂÚ6ý¦ÌJ:nW)C©øý>{­îñ´°ûå®R\¬Ù­3Dɾd7¥µ¿F?H¼\Zœ Hï\ uï?5–Ud!z!Ò˜µI§ëñhµN±#/$Û˜„†tÈöaùáqä³l1¶VÂèd¿¨í‘>¯ûÅCŠ0J¤‘Dm"¯§ò«‰˜+&Ö-X(¥Ìöf†#% !D è1È&çâæH+†›ÂP¦iÑQc7¡`«; sÅJm¥bÑ ØÒUòÖ­U"ª¥ƒÆù@1,>*«q“—pÝÛ¹‹'ª§jWµZ´R»4?ÆXlàFÒH‡Nì/ÈFT†¼ü‘…­é y›œlWƒ#/ïù2ÍÎ_Db´0©Ïí=ÚßKE*ªì<¾éýYT‘VqHTA|ÒÛÿeeÛF\P…äOô³7(Y~^êš½òøÃFº|Ý×[Mh­(÷¸Púö7fî„¿Çôzf{!P¦ÑLí1Ê”9C„5ð9ø›CS¾ÏUÞ|‚¿.fŽwûÎ1ü×Á¿'ÚÝÄgs¬ÏÁ+M-¹=Zqµ®îéjm}êŽ~ÊDŠkÐÃÌR$þI¼ ÓJ¯P|Ñ÷÷™úBm&2Q´û@°”ûÎ’ü¤;¬dØæR'ð~÷òUtˆb›ìDÂI«s’vCÙ¥JEP ÜhQû„c6AäQOê«žó‰6з='ïdXùÎYø}öíöýéo•n~ª3}é¡éS*ïBÒéìÒÖ˜r¿{o³Æì=ž›tÎúͶÅÁa#/J*#4«àWÁ¤¶0ˆS×F—q˜ß‡J6HGÚmÖ…ÏðÇ•Îè=#ÝÅ€V†Æ{‡y¢`‚wð©_“(Ìtñ³‚“b¸ò¬G¯•rÓX|Ulão,þï;²g„ðãxa»D,°ç#5¯¿JÏ>GòwG#îÜ$Ѓ-#,xÍ1w@P¾[wöp øIðúüÿé¬M=SÍRMÝ¡Aô¦à¼~q×Å1¦8D7‰àCœõÿVKÊmðí¡Õ¼N̓íªaì*R¡hÈlW¦f®ñ“QF¥á=Ú¦9Ë.wì:†3t)I†gRó ¢F¨ÝpÀцVÞL®Mvúa5#ª ?3ëιîTÖ‚q‘‚±BR‰½Ì7tñ©)–˜ÒâàmÀG(eA0€ï ¡Ãyßg üåµDŽß”_’ÕY*k~²+Q_Š©ê®*KNýæïN÷JË=L™õ û4¾q×RhíÝK«nf=`{ºàNV5ÊätÏr~]Oø•#@ªtFö«CãP È4 &“"Ó¢{t×H†wüìê¤ÚâòÏaÙPøÞ7vÊ[JŽª/y]‡T˜M¢8áÍL„ Ÿ-1¹×>ƒ“çêžêh°fÑk­bR‚MdR‹sðÓš§Ãmex:×#/µ¦^ž‘,…ðíòó„‘¢Aè{îÆ%ëdõ‚„ÍyóЪ] WžþÒYñ0kNRÉâ¯F9Wdo‡aKÑ|X"´ ‚2CÉ”¬PQìM ~‚«ã™´×a#5bÃï‰I#5Wc§ÁŽj9Åe¼¥÷[n cVÀù‚ßËñÔtÎSr‘72A¹‚Þ‰Ÿ²!t~¡®» ÄT%I³iL©±¾Y-Ù³t£tiq\;’Ño®âçN¹ÇNØ/+#/äÊzñ¥ÕTR8 )#/#/ˆ_#/—#/z—óÞÐ1/g§g–sºÐÔƒž(£ lâ%£·¾ÚsÄÚðuš…pIë—Y GA6Êx=ÌWà#«" Ô;MNü]ìàß‘¬;Ì|K4È!²/Ÿ@ä”CËC´µWW|3bÑ–Šóë­ˆ71žÍ|1€ýË6©–¡¦c.«æÌ<5oŽ OÀCI»fÎÙ÷Ï;¾#)Åa,~~X›Œ3q¨ÝvÀXÀgj3+ÅÕœŠñÑ;Îs’ˆ1âsÆO¥u¶ãÃÁ'Gø·NBæåˆË¬ŽG?£½ÂšßQ˜Á,]ôÀÄXL§I„þØ,¢uH§|i¸8††J(M¥ƒ˜–æ}«ž²ÄwÎÎûAº»˜/Wl#/s•3•GJ+Mã Ù %L¡œ\hå©LU2åkïÍlgß&ji±ÃXòLšPõÏiè÷ñVq!ÖÇüìo8s +#/ZלLVN‹<ðÇEÞH­¦Y(A¦\.{jÙŽWšNvµÝinæJÓŒÈ)«‰º6øÝtÔ5vjvãBg¢$ÐÔ #/#/—`’° 3ßvUo¯#5f›ÞväTšÙ9aï féÃ,}k¶¬ÚÖ©e&†r­ƒnx…¬R0!¡¹àÓØhŒ,í¬f]³>†|f´Ã±òÜ}ÜæÝ/7M¾ãAª•ò)(c5iE¤‹Ic#5m9øëòò9ʉ”xm4Ül4j†A8ÐÀ–MùûAÂ#5hÓ·«Ò·p³à&ºVî Õ&’¸E&‡©u¨<"Jl =>M|ØúÜ4"Rüås¾z§ï´þÈH?WúXRÏ.ï„O.Zè MP0ÿ¼…¿ÝJaÜ"ŠÍ6»._‘»o/<«1•*I ŸßüßÕãaʪTz¡ú”3þ¥6ï;M©ÅX}c! ¿ D&†aùº¸¿ók›V~tÓ¬ ¾0ø|eƒ„\gn$„¡›#5˜q’È:3þcÃ|½I&ŽD?fró¬Öºd9 BMtÃÜ¿u«-ÔÇ}8A^ZO÷¦£¿Ãã ð`x¡C†QõˆV-@‘Fè‹€);‘ Hb…˜{˜¢cÙˆ_c…9E’Ý#,ƒZÙI¥€Ø @ÓEL0î*"¯àï½Å.%%°R ÈçzG1”×éiEJ”Iá¨D*ΖÍ­‚í3<6f"È,²`‘¡Rè`Á…ÈSuï‚&g·X@’'ô q(&æ52ëI|]îëúóìóc‹/c—3³|šÿ!³Ù¹ì<²†"³ãöüfDÿ—Ë]q?ãÝlú6¿½%ýÏÊ[ûá…ÒÈ’Ä#/^!¸j 9q‰öŸ—ó®LŒý±=ŸMüÕðó¨(¸òéa‰épRjMŽTZ˜XxŒÌÁª™e1âcHIŠ~(´É{^£œÄz-#5ë?èÕGJ‹(9¸ÖÞˆ¶ˆ?£–r¨gCiŒõ¶>.sÎ`Œ¥é?äQ¦;#Ýçλ´ŒÌcr®·w’ŒyÖÝÉo Þiî«“VÈ,Ñ;mGJïlò) ]Ú,x3˜»AûÇ##/%iˆß!’aÇÀñ«Hl…uÎ#58 sV¢;Ê8–P±‘7(•¼hšèWÆPa)‹—/zU#/Ù;Æhê¨Õe#5OöÝïÊå3Å¡ ùü5!ä#¡ÄFŽíoÕ¿eCö[Ü:12µº ýp:Dì>F͇ðš(wQ–ëgaÌü(0Å¡{zƒ¼Õ+«ÂBCõð0óÎÇQ‰Eø®jøu×n#/,YÑ $dŒ‰^æõO39:Ñi¦¨©H¬PYET,í·¦³ q˜<ç3¹5wæm³GµÕºÖ&â’ð#/3p²»V`îƒ “IŒ¨$ˆ#5ñ*€¤Ý*”P!*¡Y_ŒA®D´Ýéó×õŽ}´#/B<»XÔ”î Â"B(T{#/Õ‘Œyõ†#/Pyög® 0ìÁ[’F pÎ8Ûmymp΢»H]!º ¶ƒÃ?9r':”"‚ÛMP:1H¸7 †Ó0´Nl`;(ÊΆ´qÌ£PKD}|ŽßUµÆ&>ul*ºŒ4¤TÛ¬à¡%Ýq­Äk^ø80 <'¬OH<,u³#ÛÊ)µ¹³·<ÎÄO}FÆpW|´yÓëÞdLÈó¼¬dd¨ªNÝÎéD8¦3¥ë#‘¶!ΉÎ4E„€gÒE8T„#5¢«­%WWMµí‘çö7UŒB¤ „D N<èdPF-©KfP‰"GÏ꯷/+Hå5I—té¡D–•¡/TÑÇ·€ˆmGšNG•t×¼ÈuJn1-Ȫ”%•É•37yzI ,jzüS&|YóÚNj…ÕOªê!äÑåR—(ÏŒ«òó¨s1ß>+#ÔÁ€‰Hˆ Õ\^âÞ¦„*pEëÎN>>ôŠ#L(ô1Œ#(.UÔ¸O{ɹÓ5D¢VÂ% ¡R] (׬Øó^Y¦O sУS7Å›`–C‘wÈ¢*ᩈØwÅ„NÖq°V4Ï=¬9ÚP’ ôa=J˜JúèC#,õÑAˆH=ùaఠȼHÐ}›=QÕ‚ )¨ÉÁ†tâæEÚékag=LÑ+¸ ÀMä¾ÂÞÃ)€Dˆ©Nï$[—”$!!&A… ¶àûº#xK"¸æÍú,M°¨Ïƒ3rçÑr4&z8éëJ VÉ›XXž­ºã#[•€`¢Z;Õ#5„dñÖótSO+k¸s!u~H£óíWZuË' e*:*ôÂ#5·ùÅóCr 2'o h(:¹ôÎϲ`L™²ÀwÝ@ ‚‡,7–÷ákW*ã/Ü-SÛÓKUB¼ÀZ#5±SB†Ò6é¾ÿ$çW(ŠÁ¹M‚Î%éCËÌùð8éÄÎ/ƒ:ì¢f‹ºêgÕæR‘þ<Ú#/1³Thõzõ½Ž…Ê®©ÑÉelUdà‹ÝÝßœB’“D¢H$ mTK=hŠA0i*zšëMË[ufr­7 63PA™ã“Ý9² N®ú-ŽX󹌊t;‡T±ñöÙPË1ømÝó³2RH(£XxM‚üŠ(”–!2ÉJöœ9ƒ7>®SXøžÎÛäc$õÆÅT#èær•8"p«ºÜyK}³ÊÛhŠG4!…Ä'–˜Ë)Õ)d °ÏÖùtó-vEØ…ª…[â÷ÙrCÙÛq[¤aÒ¸2.¦Ë8¨#/…(Ê6 mžJöU÷ùKf»QÂLï¸:kÁ’ȳö‹>‡~N•\&µÍ¿2BV~c‘˜ÛË7¯­¥ùãnü¦#/½­¸›üæÉŒy¢“ÄCØœ›4 øF¬ŠDA†'¥iT¦„Ù6¿èº8|#52Å"šê),(5#)Pö¢p ô9QòHmšt¯‡Çú,ë‘\tváv–‰ÛG.K} vCŽÞEâ«ÜÎŽ1£d4>?P=ˆ¾¶•M!5H«©Sà÷»Ñ|zë¹ÃÆÊ~x!Ë£»4­7ãíŸF'é’qŸ0ƒêìÎt,#Ñ÷¶ØðÖ[†A1,Xwl¥áý#5›ƒ{Ø‹KK9l£‡wÜ·#,ÝëÁ™¹!×I'D:u”ó!zÐÖs,ëÃ×ykåälö;ºÃ@#5L«qž½ÅZE µÅÙÍø̬#/¡uò‡;²)ÄCnûôõuÀófµ´5W´ˆó~£áÆç"&à±Î9Õ)Ò#ØA„-3aüFðÃ@J2ˆ÷}šyÃÁÎB;Zv`äYÔƒ¨­‘m„”ÒÅ£Öº©,É%)š1D5$:±0ðlT¥ªL—¨Øš57ˆq•‚¨=š%$7š#,zˆÞÛ˜“õÄ4—õzm‡SêS‹ß†t;ûùËJ,Á¯ìâuBuR!¿"¶Tï‘Iȃߣ5~#y5‡–$!¹t$à‡cò¯C™òlzEu]Åu&fr²Ëò^»j`…KJ ÐE#,Èôò–‘Ñ6z~ç ­œ›¾M‰:êƒÛ A2›ÉAÇÏNí»vlÃ1vkB>ÖÜ€–£–œ¾êسµÍp?tL“7‰1Æ÷2>9¢X’˜Öã+s¯:Å€°x~ÏϘdcDú>þXüœvÌ~ÿB¨e’¨o{l¾P3Š\yÑEºzC~à”äUdM"aùi¸K£š—ý¬ÌÁÓ(ò£ËËSû&R–iâf챇ùmåëÇÙÐÌâOt ½<#5VûZ:ï•»»\œXJ”ùžääÄÚ%o|©¾~À&b º“A+¸«: @Ï#50‹Y™ƒæ3­»ztÿa/da1A_âýã¤?Ô‘!!Êa/à ¨ù¾ÞtÙ]a¡¶&»e‘‚x@=æI®ûXt›õ ÂÌQBòüׂ“áeuÒ¯ÇÊ¥³éñÐ>Úôbý³Óm¹EoõÖOê•ÝÓwYmuwþ®'.pQ̆!3só÷œ»sñð»„#u1¥ygW[Ç#ùØsaI þ$¤¤(HŒ‘B¤?£%6¥ L¥þOyaýŸíÂ1÷úZ¨DoH˜j/½Øúañ|bB/ç‚P2w3ä£ur¡ ¡Ã¦;ÁqÓHq³äKD¥–•[Ž8V¾Þ±«˜öYŸ¤›~•ØÛGÔî#5šÖ2y_–Šçö~eÙÒõóÿÔÒïÞuø™†‹]fW—x€ Œ/®éîý÷ôá}•?ƒ30_¾}R«6-bj¬y‹;#/’`o§~VíÖ‡(uD×Y_s Îj&8Úc_¿yŽÌèfø¢e¡®G4·‡ÊŠ$Ôzj¯Îø1rVaD£ìkZWGó¾-·çg2mjˆ•Š¸gÕë}agæ+ª%ž®Þ³ê6açãwï1­rN–›Ns˜5311Pú`ŽKt¤yº¸æ!èóËÇwoëÅ‘`NÌ1/ú(KôÄkøÞ*²>²Š$>@ªˆ«ù~1[Þ+óÿ9RqXÌ°Ìr…§#,&éùúµs¹¥Cõ¢R"é&H<ìLjñã·¯œ‘º{ú(Ø».qÑAãºÉÂû¿Ñöc?‰.îöhD d(ù|mû7óê#/@OÖÈmLoO`•^.Þ²]ép0ÕÒûttð†WGš)xt áy(kÈóaöÙwu+°uÀ-‘³jß®Ï(ñ¿‡šÞÑ:l ^ÔuyãÉ¢Ë`Y-ŸAßêÜm z5ÄìØy½òT‹à禇˫çÑ¥"ß— ÌÁ'á9´äiÏ#5¡–´ÖÎÎÃ|©›fÇ"ÎÞ¸] ¶1z¬ë1[ĺ•|ý®®Z µ¤Ó’"kmõY‡lkø=xû|÷¾DYe-bBfGC\¦"ËÉ.}yfnûýÍÞ½ÿ5“Ïœûvúc1#,…† £E˜b"®P»–æ>!#ÚÞ³Ïí9NgÅ®¦ÓLOpÞ¶f`ý ¢'ÞÆã‹eå3šÒæÙh õæ„€’„)9⬣u4œ9À¯ãßsª"➺_ÂMÿÙåöÞ7Öÿ‡5ö¸ÒùLÃ.ì©"¸H¤{áÕup”æU“g†:¶z8]›½™˜}‡ßš2Ûª!Œ·@¼Ç3‰<“Nf?¿œi•A¨àÅ^½# ¼õ©ÉÛ<ï^Å¥ÎÞhmöÞ-4î¼3#,¬ßÕ½#/ÒüNªÞ,ÿW?]®É߈!Vâ šæ ‹¼Ãáà=PÌI êEš­/l×À’0iQýñ¿é–õû³”´wÕ""ª‡JæþöâB™PKE¿yŸžùçµæßþ¹®>Ó'#5|#/y²‹ð—;ÇÓb·sŸ*Ž/›öΠC÷xëƒñŒ’ýo‡×.^NÊ”:2t¸7·ä¸õ²OĆ!8KÁÛ’¥r„¥i‰(<öA³aNbìñ®SòþëÊw5Ú/ã¬Ow•Ë<‘0üÞ„z*Û/1IÊNyàÛõ¥w4Oª¡Ë^]¼/ ©/„»úÚðLz Ð M–óPÐòF×ù$sžHDŽO\°OQv¾/Úáªuñ\̉zà}Ûð‡˜ƒåÔ“¯Í¶N§Ž'¬âÇzžñ&]›ÇG:ñÄq£®ë~|Á83M0Ŷ-^\{uïŽñ×z+îs>ÛˆMgÙUùb6vLtOt*dƒdtºŠóõæèj·C‰~ÈyRãÈX¯~Lw$êéjãΫJÏœf.(ó|v5|cw1†”YùÝ%;ó‹;vÖ{ç]ëSª0¥“¨{ý#5™O~ªìù–aÁSúÞFÒº}“#5 SSa:›; bî4Í‘ÓM0–vñ®•8èL±ßðu_z<ÍQCØYª-™_¢eœö“"¡Ï¯# §ÒðÄ*‚Òð6¦yI¼GTWUñöò¬·/dzI#;~÷?N€ÀXó‰gíÖ©‹åðÃ2ô´ª3·kÓÀ…˜¹­1Ý›è›W®Ã¿.’ýQµlUŸõ}à«¢àu#ù6‘oÖé”ùÃZ¤î|üeyš¹áŒ|ÓÃ?u#/äˆyÌž0yÉ e²Á©}?6ÃöKžk›öò6^÷Ò$d›žÓ¬ú¡¬õ}lªŸAd0­•÷)¹eÓš*ŽñbfŽ\=~]H3ÑËÁGY'œ{l;üÅ8,J»Åž¤c.ê`ó—gÃG©½êÁ„çƒ÷¿ö«3õqà[Ci30öÔŠFª^;ÎlËškF½µíáøíwX56ZËY}.ˆsŸœušÿVÌŒsç9§wÃWi&yZ_ycé'-ê0ñÛÏ̽%êí¤uàÏAž«¬t?whlŸl?CUÀÓôÙl™ s¨#Ê·š¬§dÐRé.)NÈøõ}­¦r“öGüi©Ÿo‡6#¥Å)CýW_™ègß. ì,O/óD(-_ZÉA*9ìæZÜŠpÔìñ‚­þQàÌçÛeÁ×$n9µw;­-k1æ#/Ò·ÃùÐåÅÄ1èSñû2}j—Ç07ß?†ïŸmKó¢Iîìì‘lÝKpû‰Ä—æçÓÃÁÝú¹µømÛ¿Jç‚©£Fú%ü²ú3Ÿ)m5S0W–‚k(êN>vZ´9J =–âT*õ;ñRƒþkÀÍ á–¦·R^4ÞpÑðóï#5~§zHßÚžØ$ÇO¾p÷|œ-a{#õâaöýPt΢‘j+Vß3¯ZmY·EÛHÂDÚÛ´Bò…ñ¦dBi]×k%ôKÊ~âfQxô & Û´P“Å1¥8ƒÑGâ ´Å$¿äjêÅ‹ò‘à%ÄÎ{‡“éj£øYB®DÂ^Äi/¦ê8Ä0(O¬DãÔ,|–-‹$ʽ³—­.kstÓ‘Mâœ@Æ/(ì‹ðåñ 4(@vχ$ß㦔葠9#Æ_#5…“ôÌtMô4aþ¡G.ÚqÇò"Iêƒ";¼ÆøZó~”±Ë°#5±¹Ë£]õ5äÛ/èCKK–S¾üdûiÁxjÓ!ÒëqÄ ¦mäAìŽV-Ñ#b0ÿ9¢P“õUphÑñn‘kYB.Åã'Aˆly! ­ýw¬ÚmA/Ò³4ŒÞ¿Yþw6¿1…1>o„7?\/ŠM„‚ˆý±<#5³g‡‘ã"ÿ«îêÍÑ ìMSfé‡ZtžY´>õ”µ"ÙY:uè©×í7R§#Ö9D‰›/;¿+ü:ÈLœ20ÞÊ;ñ¿.ÎRýUß]§ì>öüM•cNnIï»ô#/OÐËâëøë峿ž,MÂ61ú¹§ù“Ë}^õ™¬'[XŒç*« J¼ßæ|wªŠsiùHÞ´½Å V’<°>ì³Oáð›Â+¸7þNE…»‹È„œ*jýI@ùJ„Hà8;׬×sº“uÁÂå¡É 0bî.ÊT÷À¼èôÀºSÖtǪ¦J+3½N°dl%ùðfg kD×Iú!{Hë_wçÛ†™Ï¶>ØéÎÇeâã(nvç"ìÊ<îB.&¥tÔwtô˜ëCYLæs¸™ª­$Ýh¨›±e—de^V©“ÊÿÇÝŸZ«Ûku$$Gpq3 Ý€Hb0PëÚ‹V#,°;«ÃO­=zò&°¨ã¾ZÊe2‰š7ˆ„Ý&q!þ±ãY}.+ÐWæ/b\îñ œn&݈vWdH% š=2ƒú®¹†!ð‚-Î5·6÷Ál÷ïÆCÜÅOèÉ@&àÕ=Js†¨Ï#,y]a>”VmÚ@ÂÊÕ{­y •OßAׄyáÄõ‚„× \ÖÞvæ,ÖTö²Ùá§N-e{ed:*gsàC;º¸Ü'ÆùJý~lŠHg Í®ð¸E¨‰W7‡ŽúŠžu+l¥­uSf½MÆYÔL`fe‡#/k8î± uŒq°å‡›9,èŸx$YG±©ä‚C¹ßMp­@üpÍkI·_luÌÇPaw=µ„H– w>õÉ©;qx,ÔñÈÂCÊX°L!T2šj‹vúˆeÛ.l:¼Á Úesj“5Æ2"X’~¦·O—¢ÉØ{{aϧy¼}á˜Û£ßÑ·›nÒVKJ3ÏlõʺÍ5N³¾Ò–¾Ú1,iª¬Ú{lÓ…åÚ³ u¯c³Ni0äÅtl_µ6ÔΚŽà#{³“AÁg©í¿<Šˆ{]ÉÓ^ÈsüS™)ZãÂÃij›ÞŒÄ“ú¸@è]ݤۻ¡}Ù¿<0UÆ&„Òþ¾¿7¼õßµ7È„u¯0$þÿÂ/ ˆÒ*‚g¿¶s§¦…áš¾}+˜óŒ 9ð7Ÿ‹®uüE4×J ï­±eóûú¬OÖ"&ܵ*£¤NžÓa•›ÄšcUÈ:bðÌÖxæÞxí*G¢¬¨sf¾&#/f2ÆJ&fŒ­·EVú‘ªfE*ä8R¦|¹çZV2©r¿«„Ý8Mý®Í«¢K¦p„þ•`©} 7É'˵U³QßùyØÞ9åͦQ#/(8z»X°Ìi¯KçÎÐÒe(U”W•0„~³ãøyÿ^¢ü(:zKk¬Ç’ ðŒdÀðV¨jÇŠ™û¾Õ~ënõÞNÍàã•ÊE^ÔúPù4ÆŽ„#5žBGeS‰Îá ¸éÆäiØ)ÏlO>hfëjÜj„ô¥f )•t~×3ߘŸ é BS/£j–n¬+²oº%ºCK›,¬†™k¿UE.ýЇ©šH”VéÃD/$ÏÛ»ÅD|¤îvá×Ý>}³ 1¯(Ç»àÆqš·¿EÙ°¹e% uÚ2öž§µ•ëÙi˜ÎÅÕ,Ѹ.'g±•b¹9€™YV#5ßk¯6ï#5êø*tòž0å#/2ÐRÀJ²Ÿ~Gó`jM½]å‹beýqKAJ`ñb¼­¼¿i8±!#/wî=„+-ÖØW8#,žp²CûÛˆÒÅ\wñ_Çc]îgÑ¿Ùõä_t…Ýy¼Õ×êßÝñÓC“9YH°fôQ¥þ5Œ´(ÛsŸ­š”›Í6ö8e6Äîè…;iøÑÁDvü7:L²í•ÓŠƒÍ:C¾ßÃŽï}y!!æT}£E#/înE ïQ†×ÔÒ&GÈaç´õ Ý#/=Ì4!´Kkúiÿ•†™7ô§s;cQ(Ò>=9\ÌeVŒé,¬,Y9µÜi@{}6ß_ßôÓ¶ëw0RDXìyð0Îh©ê~ú¯=O¶¥6Ê{k1¨÷|=9ñ©XÊƶØd{<³È½ˆRÔi##5;7¿ÑñP­‹R«”*&vl¸VO^5Ö5Žæq&ïèYè_f_ÑÆá‡óãÆ#5mÉÊÚ•äÒþÝ+ß FoÓœpÙŠvcgïøUôÈÈr°R~¹ÛyÜ"ij!”4f¬ä‹¢’d’K°™mí@(wÊaá›=§„õÇ“³®ã&öéQÑ\÷ú'âñÐ9Ý‚-ïß·®²GlM,]ÑÍä_Âyý?d¾÷CW“6#/úénP<¼å¹ú¯€Š¶W•ï6ì­²)†î9÷.¬9>sl:颅\†`ÒúgvÕ‡º9í>žª®ì¢YŒ0¡kË|`ÞÉ6´ÞéßìÕU¨%bÔ[ya·>ûé,ðÙq+Œ­Ci1b©œêíyØë3?TïñÆ..ˆ>Àè)®ýèÍxqûuÏz®Iûúo‡ßÄ`Ö5qŽYøç"­c0¬¦‡c5¶û´-à]¡àl„jÄ1Ï{bÜ…éÙšcçc‡GN=ýˆwä“Fz^`Å#5ç9HªÔmºj­Tœ ÕõDÈùNÛñÖóÆò#~¾Œù<Žù„&}<§åpÞ:òRÝQŽÏ4¬Œ3?–åœÙ½rÕFøðçómwÊ5èÚ@#<Ô–éPúS&79\Ó*E6HÜc>±n. E–gÛÀ: %“È{û°ä½(Ôa©#/Ó±8kŠ™‡nXÔÅ–ÌÀœS4AT(q»«Ýž2­Ÿ¯^M}PÀºm͵L‡túUø#3émk$íØä‚„¹üÆ¿Ç¡v˜Èº_=¶77˜ ‹¦Äån–âÛü æA ÈÒØFQ’›WÆbr%ºŒ)Øpr17ü çXÛŸÏ~è:«Sì~Ê:æíµük¶QË´88öÌ„¥q®0§kÕþ1Æ3ÄÉ%¿ué0#Pè!ÂÛ(¿##œÛn«jëŸf]š(ñÐh0r½tæ^ÃDYÑU{á‰ÛU »jz e~0Ž’^+c´x’ŽXuFOUY’Ëbpwù û*¬¯wIásÏ Z.7³A&ÐATiÆ$óÔtç‘ny[š«­ˆ_qsóK®·k*ºüœ9§ê†ÃÆ&Û`äe´>U\7ì¨Â&ØûËGI8å…û~_¹´€M’¬Y‡·Áù+•YcQTákC’+Ðg.ËÃUò­óÕ˜Ž.ñº4Ý5¨×œ¬gµÊê™S{w3ï¢ß¶ ­™©¦nªÔ·U@Ó\¸±l#5Zõ,Ô,nûdÆÆÔqr¯KŸŸ[߈jî[›%Ÿ&ºøLPIªä~býºaûŠú-ÍÈç#±¹€×¶,Pæ÷s®€Á0æÑeØ^8¹;=À'•¬=a Ó£MÛÔý9Y‘l˜V= Ê;ù˜®J͸j¸ººVÆ}ûg¶~·Ïfˆ;†ÄÚÆPË>¾wŠ¸Ò맶è™ðìÏù£ZëGÕVOA@%¿ùþ[?GÇñžÞóÛõxQÒÇD>ˆ¿ÒF¿”L+Й‡›ÿ›ÓDð˜ûâúìBµÝq< wõWÁ9äs¶¬ÖVj¢ªÄàÊ<ÎS¹9›-[61øØÓ_íëSïòóY{ŠûKãràr—é÷Û{yc‹v¾<ðSŸÉYâæsyV‡Õžâšr_e’ÌòÏûŸ§Ço›—Óù|–~—ól|Š²ð ÷5†ê¼c¦÷~‚ÖoÖg?ZÇVœ‰²°¶Ò#/ü ÏÇ–Z~¿ Xƒ¼áôËö&ÉÓeÑá@ªvH¡9” Ç ™–[_ì$÷~àt¿»6}ž/©\ ö°~ €ßb#,_`éÆ#/ο°`M™BI?ä/õÓÁ#,!ߢ¾™°€åa#±#/é‚6%Vá pU",#,þÇü¥ ËŽÝ³ü_Ôyk«·fDO+‘h›k öÁØ­™?Î=á6˜À Œyu£ íéÞn4LØv7*pÄÔ2“_vC„JÝn5 B"N¶¨Ö˜IµEœoRO—Å 3¹ Èz1~Ĉj á‚U1–º¢¼÷ò[§`áâÓѱîbv½çï°è|}þ Åú—Ú}®h£™¤=¨;!œ#,ÿdE, Þ.àø“­LÒËHæÆåî}ÖÉ52U¶u¨Yþ'צ* í ÞôÂêéú69¦‰èܼÇäÄAO´Oñ.‡‡øwwà¦ôé“âXñg\ÿMZ%Zß²mǸ°FG$-~AæþLéyOø/²í8Ô;:#/¼Úy >²1ò}~G×6ý¯SØ„ÑA`­'šü'ÕjY£›‚A"a‘KQ=eksý=ZŠ>'Ì2†ë¡ÌÈÙŸò“ Y*v}Ð7Wæ=¸¸;µçÃcUŒ%b.qJžº“XŽ\@"âØj.6IN‡Œ$6$ÇRg˜R±Du€Oä†AŸÑ<Š¼Þ¤?ËG¨bÿALxü*UVªa‰‘|O¢˜ÖR<¸€G°7®Ãknà#/€Çï$êÕ5îFÕïSqÜè‡#P-"†Ðñ²ÁŠ#/” )"b€N²cè{Ø\`pRƒK$aì-“"ÆÐ:\ÄNkÌ×"G:•°»°„,dLXÂT­J#/p/hÂQh:†÷çïH+P#/ Ðûâñ#5Ý™y‚ÝÕ¿õràÌ‘DÜã¡@Þ’¡€x͉¿.tJ‚†QÃBÆa2D‡ýd¹ÿ©ƒÝ ò€€xû‹—˜’#5ÒV}xT‚1òÿ+í‡ß\kƒgDÆ‹FhU<è…ÇCßá{‚JXÿÉq¯Õ¿Ï:‚ ˜(8£û<³êÎ&'[_ñÌõ³}fÖD»8RHç»?¨¸û]Á; ¨F#¸·yó4I“O}r$Õ’Ù9J>¥»äpææm9 Pþ{•T†ðüÏ)LHð§qDBD"S.‰Å¹Ÿ´ÚàáØëSHB3B= 8?ê—#/©Ä²í7ŽÿÝysµRTõ4ˆ„ €Sä4§äõþž‚ÕR„ÉƳ¼¥rl™ßm4ªUQÖ«lÕm ÏŸgzâŽý¸ag?q¸8àáæò8Ø?½÷ñqàGৈzNs‹,Xï×iå uÐÕ9”r4  îw`ä]Ñ,Û͈ÃôC­ú¾û-ë~“âF*^ƒä=ŠCÈ$%¯Rff{¿–š´6nÁkÝKIk}/òeŒʆº(ú&î?”û¤)#,ZÓ`ùT×ÐSÂû®3|s§V¸L[¡WwtÄ$N”Î êÒµ~à¤aA|‘¨Ÿñãäû!ƒ»KÓ4—iž#,,±*!¡5@´DÙ‡Ûgpøï]éá8„Ô‚Xd#åþè‰e¦JÓ¥aü”E"E‹|ƒ„¤4ìÕ:#/ú_¥Qqi,BT<Ú@Ò#,‹¼6?i`>|vîÞ#óaGÑ䘮dC1 Ž¬>¡J ¾ï»þ·46}¡öðð"*’™çÖ¦Ór‹Nñ‰Å³¸ÝP ëÒKÓb]ñÄ; #,GŽöŒD6™ø¥Ÿá±d6Q¿¡bÀûf(h©¾=ôÞé4¶xÄ—9°ˆŽF)R 0ñ‘Ã2‚Á!ÌIY"ÑÄÀ{ŽOwy_#,ÅÈšÄ>#×äଠ²‘Fm­Ïð~n5UB"{iÀâœ{Ty7, ?_ƒØ9¸Xf&f4e°³ØºS‘u0bØ.„ Xr!ù_¼ì7œûGû˜=/#,taû¿«÷÷l” `ÿ6…Ïß#$3<£¬m¡F’XÐß =.Y%„êüz¸Ž«ª¿#5Íùnî„»ìU[Ãu<ôhqkÈrªÍI ÕF³5„+Œ|‘ä|¥Ç·§HÞáI®T«|˜U3z5›–#ù¨‰z™Z›&`™±æ\ÈS©XD"“¦ìx N‡Ä£uEÈ÷iVeCMf°»D!cGùX@8l|߯èü‘¨,¨2"Hª~x¯Ù”ã,"s0þFŸó3Í®¤þ¿·ëõXö>ír6ýí÷W³—ÍU¾¦¿¾tvë€μ‚wCÅ¢$‰G$’I––ÆÉ#Ÿ`~ò xäa>Ÿ¯ô£ùDyý¨q§‡#,Þu(KCî!ÏÀ´%±°pxŠ‹_³G«K•ù—·7?h(ûãw|Ë4™fÈ‚Êñd“B¬Þ‹³XÄÄé•}Aá¸ÉɃ8)ÓID¾8ânòHàîât’HßUå]äúO<é'Wpë))¬éBŒCxÆ6ˆ,M^#yk#OFü#/Sfœ%Î;âSÜ÷‡-Q„Søï× ÷ÖgäÇ#,qô¾œ„ÉOHPÑFã›ä!dã²€½ø~•ö²Ð0¨‘#/ú/Ï\èn©¦æ5š6š6»¶×Ÿºû™ fï@xÖ$ü´ºü™Xƒ^æXö½€)g×H#,QT¹°CÔPô>ÞƒðC××÷?3ªOˆ›š±Z}ƒ@pH¾9¥¹‘#+ñ9„ ~Ç~¯pm>Þ ^óùè€F4äw_øŸ¬Óêlªr  $þËý({]›;Óñ7ˆpaÞ¬{E;#Dc‚;€àøx$Âd´lÊ6¤AÓç]qñwžAÅÜÑ€¶}¨Ïg"´DÊ<#,çá;ø”Ú4ÿŒê| ·pœ-ý9ðžÑPõ‚‚Š$!!|á4ȳ֧öKpx('™‰B&ÏEŽˆx'7ËðÌÜðäAjXúG®tŒ"± :dñ‡ÕÅCâp¯¬#5…ú»¿ã )úüt€_d„cÙù+ò }’e¯¾×Vñ=ÁàŸ(¶í~t3yxœÝähŸ *(kŒ-A[0ôº™6 b’z™$’y€\U=@f{8ÿ-´@µÊqͲ4dˆž¿GëñH¢^}ã3DI­VW½´£Ç»?¡>¾†  òå–Û¦rÜ«k|[çäÿ òÁnzŒã$ÙØF$ö·Añ‰`(2½0„*±u4Q“ŠÊÇf#5BËß…44ʽ‹…~ºTN;:šÔ)1J³ÛíøÈf &a™êQÈ$Šª&$0buq’ãÆñ#&Ën¾O5›˜NǨǠm}i§-î#,ˆ¼ÄöOg{®C1 ÷xX#/Ð_Ϭï<Á*B2† ôv;ŒDùÄj¶©vª­Kµg«³ˆ¦'¥'úÏX€ß0>¬žðö+Û¿„èêASÄ#,î6‡nÆŠrt`9+ÀR‹>SÅþAv%¿V^}MLÂÞ7 ‚iŒ3¯Ò·ôýÉ?¼Â)*Ÿç»¶'úmYCû^cCh:ÖiÆÍH#/3D"G[Ú¾#/B31tÔF®¤(wfC‡·OÍã«jç6¥­r„ˆîy­ƒf-`"÷•Ø®[ #,9RÕ|;ªªÙ,p…\À£þq6{˼Ññ ìó”*{€¡ÄcçxÛ°Ùô}FÓ`§0ô‹"¢)*F˜ >òÉÔý#,dû1EÉŠn‡e)Ô}a2}¹ÈÌ„9”hhX±ƒô\ì¹ÀðõWî»÷lËð1¯CÀWÄlŒhãYy°Ì홤w„NÒA$D"DŒQ ˆD˜Ð]È~™b!ï¢.Ì…ÌÎQgÕ}ÞáˆMwÒÕç uœ5@ŠbIrI2qÆöºDfä̦‚‚BLHzѽ`.~´P+g“P}BâQp=èþ„Á"#5° ÄP~ ôXŒ€ŒŒ2bÙ`’hXPÕèR@D0ƒ!#,báE&I¨‚dzý€¡Áƒšj4jZüÎì`Ro<ò÷¡âfFÆG‡Ž›Êø™&AB<A#ˆ°Ü«JQØÂÂõ_j4OP~1)Ù¼ÑLˆ}9šJ#5„IØ­ ÀˆGõ(þ¿ïøþèG>aöCÂï5AÈ#Uçì+)'µ¨£QÒ¡@ ±’m&S3oŸy«çï[;µskÎÜZÕÙB#bA#eSmw^xê³Ë®[Q‹{{>!œ9üíÓè«$Û:ü`x˜ÌÁ¡£Z]kuýà”ÝpËâÌA>²@%®Ð|Š( BùúÞ#,ÃUØÄ4´Ã°Úýú)’^ ”8‰l†•B‚ÁB) #56 ‚%›1Ä„:hf…È|è⥅¬”ñoòª²»ùÍ㜠=ïéveÌ^à„âE¥ÄyÑ¢A ›7'^äýSí¢–q®»´2œú¬‚m8…0-®Â‡lB¶É£Ÿ4{³ž^Æw[©š˜ÛÈFã#,Úf’³ƒ˜ÃÑm=ÿ zÈwwXÊ•®!^òÔÑ$¸Ù;Ág¶¯"ظn骔¬UQ|7¬+ýÀ?nÞÃéC .Ij^TÙHØÛên˜…}`À‡³cbåôoZýßœþé1†Ñ6}âØ«KL© ä â„‚#,ö$÷ &¸’O·êq0Y½¹‚ J<äÎ$ …Ô2¹”Àd£³#”T¢ƒ@’sì}Ýh¨€"r#ÕGSBPJfXâŽu2rŒrÙ±H&*7¦˜ "?P>'°¼ýžïп#¬¼˜“áÂÊô;OºZpìÙÕ!b÷Äã›$!·½Ï #äUóx÷37^Å7SÉ°®¡øï%by寧 bȱ;Ûõ¡PFKdZ˜*ví¥Š$™že¥ìÃxÉÉzOgÄGê#5MÉÁ ì.JGDÅþ|ÞôÌ¢¿RNÊ»Ë%ÙŠ+.ÀÄ©%#/ `ÄÀi1;½…&¥ø(­ÆrE„2–½Û# þq8D\èk\qÙõxø¦eŒ~¤`9•hT¥ŠÈ‰Ì\#…æy¼0nxho„H‡AL““#,T7»Ô#/‘ ¡l÷í-À#,@Óc¸?R(ØC·ÙöÙ.ŒL-ÈúzÎt fÌǨ u×¾øMœä" ‡ê²¦#5.€DìÕ û½X9ØrÄ:æP•×ˆ{ú“È;JéEX`“&@€YØoñ³›¹Ç¿4¬^m@ª1K ˜Ë I2mN/´ü›á2Eh®0­ÜŽ(¤Ùë^‰ûåI}²,€Ü#/ÑÞçP€‚ $4) zj™&“D);&$M¦ì/é×9À£¡ Ñ£D&èä$ZfÕÌ1úñ2:§6A¬å‰<—÷ˆþ¼£e“ë‡vÕrlwìC5s<%j¼™fgv÷§' ò´Öm¶3Dì=çëî”ðów]Ž£S£Ö{RD]‡#5â¿&Î.Aƒˆ+Ū9Wa¤Ö—^¢„Ì¥úÊ#Ú]§@ø$Ä å<“â—ëÇPIë§øeI$ÃŽÄ ;”­l h7àän\ÿV·· L‘ÓP‹É• è~‚(êgrò7·F0ŽrF* àF¯@l¤r7ÞŠà~xOZ/$ØhW§0øb˜¸Ñ ’¦%ª-‚¬LsÝ×ÀáÍíÌìsFEÞQB=õ´ÔÇ\ŸWÜè…³­<³·L¡¼îOÆRù!¨e/¥„ëõ™,<¢h<¾(ÖR,RåÜ#/™ò»w3vA&Ø?¬Þ¿O~÷¢Bâš¾"võÿRƒ3Ðn`ÌB®¥ÑQýñµ}/ =¶×ùc˜^65iÔKVäÜRMΟÎÞGgc›²h“š‚L!0‰IÈyÓvHðGiÕDçi2LÐ/ñµáTÍQþ^G4ìë ™¼Xtà†}p‰‡fì€ D Û|,8ݵ|²Û»ã†Ü3q׋±½?ˆ$ÏÖL@£ÄFL+¦Z÷‡Š4³FëŸ Ýt^ôpBÄ7£AµÁ#5CèÁ-4Hðp\ÆBÀ@è`©IõGãÞ`UîŽÓ LñûÚ(Çìˆ&\ ˆB$` Ÿ¿¨ï°.ªpmKÑèsîL»ÊÀ²à& Z*…ž‡¨àí{šfÐT7|ÿ­ÇÙ Ù=C¡ÍÆþœp„œbùôq¾XÆtL–?ëÐÄŸ€=ŽÒÞTVrG°.µ€\À'Ç8b\ÜÜàpqqÄp C¼È ¨ä•õû‰ªü{¾W€n88£·èJn»ÕG³Ã`Tv°)$+§ªüW¶ ©<ÂL«!{+ò>¿ô¸¤èU–#äˆÄíWt£ŠÅŠ2-µ#5˜ŒíøK•W iÛLY­À_Ù²’#l #¬­ÝŸÅµqíŠns¯ÞT‘¦–ôD·¿ðë?aÈžÔ­µ[i#/†Ú¡+ÒÐ¥f¶f,‹x ¸÷Õsà&öЂA–xno¾+½+'cùorXýºõ½9ŸoÚ—O²ó¨>óû_‰×õD~Gö!W*z&ýÌ*|õÒOö¸[|vKy¿y«¥ÖZæ¦o.qÓ2Ø6 jË A ,«L¢€~A¡€ÚF hàši²nÝ´ÒKÐbDÉŒJ1V4J¨„¨Ò´Ø#4°(‚±P¢¶„ˆ+Ùm·ÂÞÞu­â6ñIXÅ^à‰F ²*¤4ÿ€›Sþ g×#/ŒÜ„+ÿx¿Ôp‚Kf·Á=8 $Fhà BÈ(f¿€~}Bc‹Y$þõ–«Ô&1tõØ4AeòbÊïÝZ¹ôpÁqy :0¢G o~H wt«GRäÜÊô´*Äts† çk *ÅVLv Ä;/¦Ã7ÈLû›‡yêÐì{ȃHDS.W?=ÿ;~c³÷~/ûVñ@è„;ÁZÁ•ßƒlýFúQk¨˜¹Œgáþx»±Ú«úy/Ÿëý3b·Þl§~V’ÀÊe!’/‰ó¦f=¿ r’j‹Ð-"†œšÝý®ØN¯ äW¥¾ïÚ“×2 t“3¾î%š'3@ø@Dá' E·t@?Þcî.Î÷Ó¿´uñQì±—#/7dr‰Š*!9ÌNcÍØqúý‰F®;O7˜ŠBbqaHÌ€íøâ>>tɧ´ƒK×Ý7–1nóµÛì ¤W”P0€™À-PáBB™Ýõ¿Œ$ÍR$’R#5Á¿å‚"Fd(;ówI›Õä¤2dxDr¡øïdÞg¤qÈ!¼w ­¸3g׶£û[ØÔ)‚¶6åhJYšÃDlDÁt¡É#>jä)PÌ Åáù=nÌ7*ÛœˆA'P÷IÊï…ü†%l$Ž°¤„dàü©« 4¤áhîÕ‹@+!§§v‘‘²ç€k»ˆðsZš‹~‚üSºŒð!¿ŽñMO¦NZ3h>"Á#,R]"Aˆ HZig‰#/yš†!ç-üا“p¥Òûù¼Ÿý=ƒ´3‡×U<’mE™^ô:Œ‘EnBø¼É8<Ÿr‚°ÿOƪçÌüËšS¦¶ï©Âó¥Ñ=;iG\•;\×¾f óDœýLŸ¬õßhcÉË ·Ã_ÐãµwsI‘;-”™˜¶dˆ1êË+aïòYáT³GÒµ@ªY¬'îqÿgöŽ`”êhiôA˜"ÈžNÍñ»0¾D4ùµ4ªý|6¸ä¸Iµ&Єîu ‚¹ûàW-ÑÅDéW2Z9{š¼©%Ý$‘AQ)‘Æ|²±šôrE—yÌ!Vî=!#/iÊ:ƒŠaË¡².㼬†Z(}ø›U}¦¶BÎ~\šlG[I–÷+_OJܵÕÙv,´ÅË<ÈÃ4¥ß"¸ú A0Ž\&CïÉÛüôŽ>é’…—£EÛ#qVyO;õ#,‹Ž&¤U^#,ÛV¿#/ ÁA¿õØ/‹¯°øE·¼òi$©ñжœ•4Šõï•Hù,ù¡ü-„ó³+°Ì[„¾´Ñý VFl/žF%¦D©œ¼Œ @jñT#õ_UðÍS$hd8eb½í¯\%:LÔPþKÔÿw¾º~ß×µ’Ú=ÊoRJƒ¸›¢ñÝÏ]<áøEùÇ©}£Ãv‹#ÜøZé®#/­1RmªÄÆ}.ÚÛ•ˆµ¡¼F¸ñ×~Y#/çË›ï}&G;/kº«7å¼Àȸ칳\Œ‹•è¦*LFHzm€þ¹ðÔ¬¦×ë­*Y·^ŒíBå<žP¦Ë°@Mü²¿ ¡G5AsƒÚc Ñj«RsÍM¦óñ™ö¬&®Ü§„ÝqQ=%½‘=Pæ“ö„zt–J6œ^dxñvÓgŒïwHB^ÀíîQ#/´Ðº;œ;‹KÇEIõsyã!`Ôõõ¡ŠÒr6;öp{ëQ³ÕU9U1#01‰QS4h5)+5$ ÏQá­½ÑÆ9³•„¼>ÐfY0ã·nýˆE¥lßì{ä8¬¤ƒŒðÕlZ:¶•{V›*_ô8T50O‘ø~>_ô¯çù«ù.ÞÑœ¸äxô¼`KtymG"­Êk#/‰&³j-‘Ù³Ò¥÷YÐa]¹ïXR»‹ü*Š­Ô,Â$îÐéï«3Q¯GeR;tªá¸ò]>§óêåm§tNxPI_ñ:w‡ÞMgð9éy›ã9Ëƈ+²¬þ7=iÛü®ü˜øô<õÑŒž1é!»ŠÏ…#5ÇÛÁ1âEÙ㓶N¥Zw¾mA„¤ßar•š¡‹“;<邺ß2 !£ƒòŽ(ƒÜO~gÆ“µ¨ãX™ž;“0â !˜sG;Ì)¯`¿ëÎáõ·äûÛ§¶w÷\Ñò‡#/s%yOnËöpȇmdE‡,0;‘%°ü þc-{‰éÄñ¯U•3ì¥G¡ê«ƒ~£ßs‡†|m΋ŠæÜfûËOÁ5qow\;>Ã1Ê „këÃþª…ãéÒc}_Xó‚ÒLÃ:nWWwgºŽòÓËÙL:V€Ï›Èk©û¹ò ãÉ{Þ©­q:n³={r§¨³7 \;=¸ÝÐT ̈†P1‚ëÀó°¡¼ŠØˆƒ”Q#,("¹³0c¹Õ’Ü^7Rá@ãÌ„û¨J€ œPwAßåSŠkwÓûÕ^¦öElUNÙ ÂBïxF’sd4ó(ä†àV¶*r.¦ÙÕñ¼ægÝ]zB‚eÑ® ¡”ËØY˜.¢¬Oš©qªzyî­ó™à°dÈh›µý'¬Ì¬>´Siá(¦a¬b`îñ&@@ó rQäyW:ªÐ¤ðòÈ5Ü’¶e2W²&.ž¢ôÖÖŠcfJ3,xÎÏ~º?Å À<’4˜#ˆº€ÃYXa¡ET€0Á††±†ƒP‹J›®·£A¯ 1ÒQLç{8Š™#,p}郛!‰§¬¹à^^©~c^ÝÄé¸t'¯ÛE³{(,$’ö±!ÉaËmª©®¢a«Tbßj;[—!¼êçtvK£×.}Ú¿‡[µwôÂêì}½ý˜Æ­íøΓöj×CYËé§Ûÿ]¥Ú4Ýß¼]…€ã×M¡Cì7´‡mÊ;Š¨®ÈÕSxšŠl³ëd¼ FnË`((YM" â]•VÓŒýA&LJà/Þ•u¶î[‰5RÕýÏúŸÐø·ÄÍe,æo•Úo-ŠÙ]cU!%¤ª%„ƒòþÇT÷´¯±N!é!Q«»nÏ"¼î ‰¿½þÀz`ÉçÏm̦Ñ6“~¬IÁ| ú0‰„! +¬(ÏÚïyr.z“y ¶KK™cDw}’QµÄáÜ쨑¶Ò&¬;,„ŽCÂøb¶Ùº¼ò±EŽœ ¯\(°B,B&ðÍvȆšÔIg1G¼ÖŽ¦à&‚6Ô)ôàÀ„ÅQ˜Iã„Û'72…ÊYÄŽ{¯oãÚ믉|]T-4d¥_¤±ï.™ 01˳³¥¥­˜vyh¦ztÛ,¾@z¬”Àl qŽLBØ3vÛ ›zÍtj‚ZÙ»ø¡EYß•°ë¹«/¯5­NzʺáŽ.éÔ£)™ömk{U´i;ÊQiL<4êh±Ç6Ïqcã×WPr·Ç–ÎûÌœ"#ÓmÛû!wh”Ú^ÆŽ¼ì\¹Äa±ð[Y·¡èNZ)©lxÒ4S ^ÁÏôds>!ñà4‘“„ôî úÐÏÌb]n&ÆCÂF2îgÈ47„ë—(ÛÝçïØe×¼4,J'j=f)c òº¨MÇ8Mý|9eÝážÝwCF $‰dªIk6l~¹zôÉ«ŠRï¬{!ñ6òR`Šj‚Ó¼'ŒD˜!™½Ô22Þ$.¦3 HQ^‹5#y…ÀÆq3šÓ©²Ä)k°y ñÃzÅ,½|c¨FÄÀ‡»ùwu  ø;#/|4Ö >€·8HkÒÝ¡ì¯9Ç\x:GÕ”jì+¼Ü—7ÙG"&È÷(p³‰éz½cÔ&¦Ç¡  §q¨¡9#s oØë¹s2,©Úm#/Œha›X}ØÀÔƒà’9oVõqëÊwx„¹4ž„z/sÊâÆ9L6mt^”üJ†–$9^Æì’s¨ bˆ;xb\ðeÛþ»÷¨¡Rw»êXê-¾HÞˆ/‹™˜S¨dƒÄå @3‡Ã§ÉÀiº¶ø¯fï׃>çÞòÐOoªq›Âc-“ ,#/MÖ,û~ˆ÷Q¶Ã‰ 6‡ÔiØN<Ò¦2 ‘1ÃêŒÙ´×½d$è„ nyJ¦ÜD¦Î÷{Z$æ°`’ L¹}îšÑ\]Þ/bê`ôØȉÃÞ(‡¸Ü¤&‡QD^÷"àš@g0Gƒhj÷{0e! \(ÊBˆgüÿ@â= IMÊ’ˆÓÆmu±)¤H‘HvŠ›@×xdÙ>d¬j;ÕÉ£‡èÒ`DˆCáÁÉ¡ÃæK-„!™fBAG 1–i¡ÇØLái:àÙ6î/bŠ-jÍ~¢˜`3–3)ÝàJcXŠ¿Ÿ¡Ctˆ3(HÔbè0#,Œ] ¤;•x8Î!=–ÐÖü¦©áäf#5E!SNÙ9æ}ÿ^Ìô1¬¶˜³Ìð#5 !Ñøê`v#/s‘ 3ë©n%%=À{#:ŒŠãžÚ1@ˆ9s(ã ÍSCg³U¡@×,l¸Kd\²SU3Œ¬X7Š12âóæ›àðVp¦ne.S`a‹2=Í‚!ñ{dk2¢ÎÇFzæ9.þ—è„Ð¥aO%PÈ·LÃ#5˜Šd@g‰HzNcƒ”p›˜¬äd#Å:úŒ7¥p)¯¯Û%­ìŒæl9æÀŒ{^ðÂŽº2™–3^„¹´Ž2l#/¡F×q[–*.IÔe©RPY.ûsÙì¡Ý>¡­½‚Åžìcm¶ª]ꮪÚ³Ê]ªªª­yüîv$:†ìWÓáÖ—.DÑES1ÃÀ;Æ|¾8)›Ä5Žs¡åZNòcú¸¾„ʹ>Û žˆ˜CnÆŠdm#5ÆëU$5³Ñ3:g²¬’lf…-Ër%a=¥Š‚MXȵ‘À ÎF%F#, q.&gEÓ;-Ùê(ÊjÌ;2a!l†jŠof÷–rBgHÇ"-3$Ç(Øñ©ý° ®z}&Žž[†úíÛiu®&yüÍÅ‹±]ûDÿÖ„ íçÑ>¶ÍŒë‡ÿ:?ÛA£É!Ú=¯šæ»û0|x®Jü"©‹"¬VEø ÿÚ–,KËI‹NwïÝ]ì#/¤f‚…¤¡îòØ^zîú¸(Ö>%Â÷.X`Á**,F"”Y*–Áô( BQËú^‚‡±ª(h”ö\Š€!œ€˜:.m6#Ÿ9 ¢Ã£Ç¾0È?ÉÃýìÿŒüŪÇý?ôì I©¦5¸ô}Ð* °}ªùzvÛgêç³k³)4”ÕÚ·óճdz³ÑÞ=ÉÝJHlü=-ï)¹#,öiÏ &ýjR“Q²ÚdÛ) M¼ªŠ"HƒAHN QÏlÆ€9J·Ù\‘€Äš±iêüÓFFMlZ)rà3íþO:·Žû9qÛ¹xÀ ¬”“Ô.^Š<,û~ä„‘.ÄT¡¨ˆ4¢=Û('ïk©{¬r²5ì{ìUçýe\s´Œa©½‡‹âÒR|“ô÷ " ÌÊ%Œ A´& ¨üs=$ŠOÂ}òå4Ó#,ŒT²‚f®ªD@7)Ø @;ÐSÙ(hÀBDÈàªÀwûõŠ™—ÔsÒ¥ÂIäÅçy°Ìk1EAPp‰V ±±ŒEõÊ€Ù?AÆ&ŒF71ýí•FèkÙÜp‡”¬LhñÇãcí M`¡©³|„{L‰ÈªM°~eŠh•:üŠBÐH #,=fG@–ï§Ù 2cSàuQhbªÛÇXXÔP3–2Ï[ jFO#, kV–æ[†íû¬þtGÉ€+¦@`àÐ@ $EðÒû¦.ôÂ(A0 $ù'!:É÷ÅaDD$E"âD€W‡È.)åÅ$õ[u·[O.Þåáí×æîÓ'Ž;tUÏÕ©•¶¸ñIlNµƒXk#5iB Ð L”€¥BÉ‘6Sü¡þnW,jw^K{"õoK)%4[Þݼz­ÍìŠö\¹175ÒKË»–5Ç÷žkÇ.‘Ά74••Ýbå>¹ÛÊt»ëåk&±OªR*–‡x ‚hDÀÞ¿¤â32FÂRbkËÍ«ùðmø`\ ˆ§°˜¡q3þ‰K`Z6ôYûÈEEb±ê?)È'²×ð÷OçÈvû+“E’c©Bu‰Q¬C²æ¡È›0åˆÆÑJÒEj+”…‹œjNIA˜,¼íУž¬Ú3œú­´ÌÚZóà$`8Àãºr$ŠšR³¸®ºõU¿ÁÛ‚Ä"ª€qQàvÐ DŒd‰"áF,ŽFt#ÏÛ³–ëUH¯ñA1&öì¹äO ÇÓƒ‘Í_Jµ¼ '‚”àe#ÎTæ0¡É® \;Ò’hEd$rH”JÓntBt›)¥…%A€~,D ˆÇ®i¿‹u.œ0ðZ‡]Æ]Ámr85Ù2/â6ð݃ȓÞ8A4ÕÁRa#5î\K!¥Ž“ÏÁ1í#/#/¦¬8ˆÚ UƒµÞ^Ø!%ÔW¸/ØNÍôòý/Éus@øÄT{0îï徨å(´­õo÷ŸöÉ»nj좈AѳLË—eÌ!¡²ƒÉ™é}gÛ—¯è(Œ!EBŠ”RuvqÜœNå”rA5OM&t¾ˆ#,°‹(´3}ÒD~@ÆÌÉ ÓBéTáØ"jÓÌ¢ê–,è'žI×—Qm¡‘ 9§ö4º¬R¾þsîÒåÚfnKšÙc„@wxîÇumüþ>gaü,ÙeTJ$hªdTwÀ›ƒÀi¨Ý …Ñó #/£m±ªÅÕFEJ$¶*¦¥ZÌ…*’¥ª-’ՆʊÍšµ*Ø‚ˆúN ' òœmôÊ“”8lñ|‘a¨2 ȨœüÅ(VÑ#,@ ôý[Yœ½Lo’§…ˆp†ÚBØßì¸ðÊT`3#/ ±UÖ$ÌŒi(ô-ì;ѱ#,%• öÀÁX8±e:b=s˜z¬#/|÷U_… ÆØðGÕšFLöM6Ž˜Ë7L(âÃsCIFE h ˜6ZEWÉZ£9‰2銯·ëÚ¶D_nõ@V@Û´”ÑSc :³—hg=é9Ò­A‘m*]%]%¤#,”öÜ¿§³Á;ÈÀs ¤D*¨€;6„OÛ¼à<û6·Þ¯¼µd±ŠÍšÌªûÔáª*Jekr®Y+–»JfÅ“E¨¯ÆµÍÕãt-q+å*øÉ+8˜(n‹#, @AÈï]ç}z>Ž±µ¶úŽÿv[Êi£t´‡ü .7|^®­ ëz¤‡*ôù,‡²Ä‚$‡ïN"“Þ}¨ˆ•v>Å:ÿÙ»W¯Ëöýœßßþ6Ú˜9!Žuš.$@„ Îÿ2ü[p˜“,)µŠ’Й@îeî½*„?K ‘#5)Q`¢+åT:¶«n;N9XÉÖhæÐ)Qÿ\X/ç$&ÏÃIeTù¿öiª'T ?‚ï“Å:›x§¬1#/o / (gÂT¿«{ˆhbn#/´bDÖgdç®A²²c¨DH¥Ó¹2l`›zÝž‚ôaWõ\Ü0ÃÚ‚ÿ„Dvvu‘dC!«çq †ˆY‹ð:°°@¨i­ŒÅ¢6ÚÆA¸¿U#/¹‹o{dÖöˆä€¶úxÒ$ U 6$cL¹Ç¬zsE#5PþÇ›ç36pÅBAtʼn²¼–¶ÈÊâaDÜpÖò±ƒ@™ª‰®ËoÑO "¬ $(¾â’¢¡ëñ£¹T;5<€÷Òž1gN\Þ€ÝùÑùØÜ„Ê#ÅÐ~R›…BÖˆ)ª¢‚±eÄb]Vž¿·©æAHŒŠz‚¨¢$¨‚ #5DH ²xü‰2#,¦B†1X#,šd==ävߟsƒMjX3ù vNˆ#5Å÷QHFÅO~Úms`j#/6À#¡}@ô±d#, ² ”`†|ÁÃë²dèììõSö¿¿åŒdoß(ßž$ÛPîÂÕÛ8º“„‚"‚Ö[÷P–O|RMamC²¬. ý™³ãsg£B÷ÄL¤ø¼Ì’ù§ŸƒeZmr´¾#ÈbÒ¤GHJò8,öÒÿG‚tŒ>£Êâ÷òˆã4g¦#xVcmÖ¿[Dõ£DøL.z¦ ò­`tx~{SsÞ{.ž7Ö;g€Öœž×Ø<‘Ô²öýZFà[ÈQ0˜I»ñ~1Ï^zí…öÉT<Œ-ÀƒÌÁ`¡O¡dΙâXÈÇ×Û²X-SdéÒc(‚ˆ: ¿ÅçMÒûrw®ýGÞu£V‡ÏlþèZn uºc:·¼ü¸˜K‡­Ñ=ç–VuAÚ¨ÓFå§j–×J³5C¡!™œ°Ä+ÆB¯Ë¬1¦¶éO;îåò±#5<>?f×ëË»¿¬щ\^6&‰3Xi[²«Œ«}š^šèÅi°¥¾8Ž 66 `ÚŽÅÌ„G¬ê;8¡ÏÅ{Ôò-75¥­ hZšµ]ØzÑä¿®ÍZ[[»Š’'Šã,͵&æfñ¹«Ïf@ J0b¡cŠè©ª&hìÏ ‘‡äþ66mƒMž‹‹÷0äÞx®C¶×Ü"Ó±‚àÎo)žªÐb/ÇòrêïÞ™— /Æfï_#,À‡`À#¦3¾Fk‡¡£ ªk FÇÀf iœÂÉ“õµÂX“ ©,KíÀ/@÷lgèpÙ½œv7Â9ì¢isÙj.uäñC˜èÍqѤ÷†Fa"Ê íL(ËÂ8Aß(2p5S¯(SMUJ,b1d;vó%ÕD%&~×Ô„ÂÈ¢Ò)qæåÝ=DZ öí¾CDA‚F`˜õI=h§–}ïjá#/ÌPðŒwßàL!!“±s‘–DÊÆï˜øéÖÇ‹1;-°à9#,rF¾TÍœ îCGç³ðÿ‘ïxüÅM}`z| jFB‡*)ZP‡ÔÐ|*¦¤o/FGƒHqB:l×N0$—=HDégõ3¿vkï¼²wÁLÕ ÀØqdæõý[” 2!§}›Œc+äv@“¯ÝÚV»„Mþ§AçF6(ç,‘O#/óëÅ©š¦µ¼Úæ)dR›%E®–ìÕÔ³DÎíµuKk»ñ¬æëºë^/àxÞLj9bð¢Ô²! M¼;K˜ ‚SIyË»‹‡y¿ %„l+[óÞÚ¾Òû·ÊhjFRÔÈ«Q5ýS» ÚÃb4Z9˜œ^PmU¼{¯.³€\^øoúêýß›„žt2‰Dó.ËE#U ‰ßˆ±#€áE0žAªuªÂQÎ- YJÉCÞGB!ß#/ÿœÀï£éÀ>#/Ó'QtˆpòÉ#,#,rÛ#ÉåCª °¦C7;ë˜i’Í=h€÷%[»,9­ŒpQ9¡lGJ©„¢æe†¹újÄ3&šîÞµï@i¥ß±M †+e~Ì+‚âŽÿI%KLžÐ*¯©êdž|vž[ïH'$'ÓÐzSê¢bI"•jnÝ]hÖå»;­ÚêÝ­Ýo#CT’7©!Q4•ª#,#/$&H‚ØYöšJ…n¢B¡‚GA8žúXên%µ(.Ždb$ƒP(a áˆ}h¡–6Ï2`4LT5BÞ†ŠÏÙ£ž°šíÀÞI Žû×è<}O¿*¬-¶¶2<[ýY³ÏiÇ"Ëm±mž¢“Ä„‡V#,¥^¸aëš_» ó#/Í\vÈHPÌÃK#,ÓI:ˆ„RD#C·.#X¤7`q/ÇÈ¢éj¡Ö™Þ"ˆ wbwX‘D#v;¤á¶–·¾ÜÐCdXS à¡Øì449íÑD¼Gð¯»C4šËãz¦wÃëøóo5ìmìxÓ30ᤆ‚ÄZît¶6‰èù]ÇOÿdÅ34Q/¯Ï|r2oD”•²,HÈu¢#/±}gžÙ#òc¦³ÅZ’1ùCª¤Þü%à«Ò‘­5)$6‡߀£nÙü!û4ZÊ»I e@¯¹Ì\\6†ëNF¶Èn…$ £äµ)φ»pj²®¥J~Õ³rçÁÓÄÆ6~~–»3ÐС‚׎¨hãÒè„P®s¦úäÃ… jþÊŒ¨4ÌG9û(!¯EUCàì†3Ç7¨ÀÃ7n ­ÃA ´¼Ê/N6Aš ¨Q¸]\è|ò¡ B)L\Âaùƒa°{yÀ…Îl§²j Ð-" ê„„¹†¢Te…,‘9¿ÑÏ[²•üñ¡Ë3äCH&%•V «YAìU*#,…EV Òõ•ªœ$:Ä’ÙAÌ»±ˆF•$*$,„#2¥\c Ã3ØÒÖ“=Ì<šÍ¨Ül÷–ª3¢P A¾50tÖ,-(¸«jÙ‰€I0Ì‹#,Ûo)MD2› l²¶¾F#55`êR“®W¥GŸû'V/¾y”•Ýe•ë‰o¶”Ë‹$…;‹ÖT2‰±¤.; ººP– ã‘O”,HéÆÈ…EÙúDk-ö6™›PPrÌQ'äHéÅ´Íáµ^œSÜ‚Æ}“̶I†MK¹ue% ¼#/OwQÄö9oÌ’þ$áJ•‚ðÌ@ 9:Àq¶i#,,óF(ÐÎ$¡è/ãíÎd°À±~F6óññö÷#ìñâöšpoKž%EýKâF ©T+n‘„ã÷tæN»ô"s-qE’(¡—Yëï?µ ’B+!)ÐDè¥l>Ÿ¯W}ö†&ìÏ™kÖ2¥ž/Ë;Iëï3Ôz¨ñß…šjwHeSR‚Ž»Áp–0úžÛÉ{Óóõˆà¦&ƒìÔq¹áãøÇéˆ"ƒTIÙá„[\“Q êbçD BÍØNKœQPa0Nmj–pvT—sÖ#Þn#/ƒ~s_áò«{‡´`øæsp«í#,öþ³±…*/Z2)MÓzÕw0œâþÍÆñdoéO*TWäð4Ëõã8Ôîñ:Õ%ì(»ÕXÒrª°5ÁŸçgøY3ÒBnc1o|p#/7Ò[sQ¡¦!A9©«.Ë%Zo»-ïÜwJ,i¬#5#,U@W@t`¯?)@M†5B¨É2KäàÈ Ú'2/Z¤K@‘KÀ¹:EÊ ÈÈ –Æ#54ð ITpÇh8ÑÙœ¶ÏO&“‚ÅWMÊ8ZÂó\‰ô˜³a¦,ûŒ…aÆßé#/¶RÙÌOÊKl=ô—a<VN-ÛÅ¿IÍ+pÂ5A´ÐI§Ã$ñ ÖFh>àÕk;5åy¤2z/Ìm”„†Á5áÄÁ›‡±ÛQvb³ûÊ¢'G0Ð c#, Ŧϗ³¡;Ñõí4¶» A6e0!Šˆ¨4‹æ´¤ÏÇ¥ª.ƃ&ÆŽ%Ì”"Ùn±¤3M²IÄ#5i£I#/ #5 E4ðÒãc=íõ[&hˆ„¢ƒö³&úA…Y©b” ‘ ˆÇ@¸Ž#5 €ÞZÂãˆ}d “@¬ì‘¿ih`û^¿6ó€wn§§à™É§U>Õ0G©{`H‚@‹ ŽGYn#,5ïÑÓ«®Mc›=&Ø1¦Æ€wNÿGG"ÿa樌^oÊàq)8÷ä?L¹`—ÞOóAM ’g’©çÙdÿwæÈ!ÒB*B"‰sºvYƉè í­¿’Ðl‹6-oȹžunIKmÛ¯¹jª6Ô„‚ÿ`E´U´E$A¢ #5—iôê÷§¼£#,zˆÈ‘|ëî:¯iÏCÜ$©[¢X7ºz°îЀm…” T“íæu¦Vzž³äqHDî!2@‘†H%2RkHS$Ì–b“lZL´¥ÕÓ2¶˜dŠ6šT!’š#0)6IJÌ‘¯µÛ¦&Mi4h–R›Y¡S)™#0(Ð#/©‰I¡ÙÝD*hR$ŠIIbb Q’PZ4M’’Á(EŠiKQ²(š šFe‘¦£1!¥„”QŠ³Mgz»ö•;À8¦,ú0Ì9.ÛœX&Aò~u=JÎÙŸ·ôf̈ÿx¡=;p%­S*F9.¾ó•e† ì:¡§ºy®ƒ~)W¾/×ÝtÊe#/´îÕ¤•¬D!$Åt2ÒXï-œq1ñ ÐzŸM“Fýìß…Âöï7€y9,碓³¾åì¹·[BJ²G6”þ‰kÒË¥gßÏø¦dþ$¬|>˜/»}!‹…aº« ªhÀ\³#y,P!pÄ ^F¢ü#, @9/rÆcPÕ,ÒSiZMa4•¤¬ËL$$,qpxÖ}miåTx0ƒQÊ™Lø1Ç?büÄχŠ­ F=DØ„}ö“Dˆ•õêÚM³ ! ZÅ¥=ÙþÚäcõÖ;ÛÔµŽÅèõ}"DcqøC‘–Á[oZ}>·rþd‡/F› …Ùt”½*› Jè^á€faúþ‹²H1Õ‹õ‘½ZŽ 1d~æí“{©?å¼WÙÝ­Ûµ-ѵ´cH%µ“#V iô8$Ýiš m¿ ÒöÛMó“(á©ã#´Ê%j7®N.ø""àï3]{ãŽéDÆ°¢›Aa¿rP´OezA¹ÁÞi‚ìÆBÝ|J{Lš feðv^`ÙGäÌÜ$°Y¶ÜKß#5<öÒ@Ø#´ê@íÜeˆ¡ú½’ö?o-tž¼;î¿¢¼Í”‘@†kübØ<<õµc/Ñ´À5kìèrŸÏèÐ!V ÒiϸB½†vW5ƒI²¿5ç†ã-†?mg:eY| ±:YPìžì²ªôS||S%JÓÄ©=ý~õÊŽVpj@¨{% ܬ!¹Ï‘Ð3¯ ôœ²[ÓïäY ¸Y#,7\F0–rÑsÇ'B;Òmˤ7#5ˆÁñÞÉ ,>!C ÄÐ$ÃbaÌÃÔ#/Ôÿ¡¨Í}¤Cog‡ŸZ’ñI–ÓìvƤPû`M2!äûFˆèÖ Ó…-Ì­ÚÆÓÊ;Z#…ê¨Xe$|2’}ÙåÞùÍÖ?£V®lÛfI”Ô†ì)ÔÛÈïZºQW¬Æo a31þMf•.¥2^BåÒy51Á„-,.K¯W”£ÔZ1ìf¬Þž1`9Û©š‰”—ñ¢LKКB$ÎÔm¼JÝ µ6üt’AIÒÊΉÂæ>ùÛw•wws!šYÁŒL\.iäÁ Tø0R0JtÉžçOK7¢©á?óíÄÁz¶¹~#š¢ŒË­;"¥àFá`ÜådçάËì–Ã#5ƒC0jvœÖ ÚcTêX~Ôê¸Ü·1Roø‹g„r¤Øóˆ3%ÃÊ LBì 2´>t#/<;;, ú7žx¦dºCäÓ@Öp5‹¨íUƈڜ‘«y(aÌ\rm†–Âz¡W««2ùZBA¢ ¦³Wo0áë”àÛq’52v¾Ø³/Á§Áuy¾—h ûº;v[Û$ç3¨â3€ËÒÇu,­úfwÕØcbÔRd,vrhÀ L»_;l\;þNq#,X(‚‡cÆáŠ(ŠÕHeûÁ5-1”êqDW;4Xa&(Œ~]çªO÷Ž‡øÞu’ÛŽÇLêÀ¼øãs¸’xíÇ)Þns/x.ï>YÌ㦫 ¤’kL:‡C›Ø‘2Ž6I7= ¼c-l‚vÛ…É%ÅÞ“¥¢*êoNÐdÝΦ—ØÆëD@³ß™9;6.Än%à‘ÓÞÇG=²öâaÝðÜì¿Ün æˇbsÎéÎ'\µŽøRž¦Î]j)XÎÌqMíC“´#÷å¼—qê.gxÕßÃ7­0ln6Ôi•Â¸Ýq-n#ll#/j&¡Ü(]èºÇ&M–7K}KJÆèôRrA‘ƒaÎ`î¦é‹"Œm¾86v¤cpÎLc+ÑÆíÙ\»•ÓZÇsl£ÔÅ\#/e¬S!(òBƒfækKZï½´k**)šjÝ”ˆJfGºVXnÁC#ŸBÞ(BÛ‚ iÐ#5Ñ'Ks­ Á\½ Ï•1GQì±mP¹rIY¨M†1C8ÎÌöC´ù†ÇG2>TP™‘³.e†Ô[ê‡FPaNü+imðg`ÀŠrÐ5×U/V@ÅCd&Æ^‚,ÆÓI53S¥¤ ÓQ‰v9‡ Ta%3 8][ßPJc´$dÂ"méÙKJ¢P‘ê´TÕºùØÔ3‘É 3ø1z®C³mΕ­·Ìõ’`Lƒo´ÚwB\G,A(©Y+”;ßtÓcI¬7rX<ð5iaNF:tÞåt4 ÍËËÕ6ÂXK›e:G #5&$Y<Ú k‰ÈCEñçÃeá—Èã9€·zâòñ³Ñú‰ƒiÈDS‚QHn!¾¬s(a–M…d&°U2LŒTBš@»©O©6f¶RêT–Q§v(bÈ°2’P¹•40òd£zݘphU°-€`ªJº™8T-!–ój¬ YlY)7ªP1šË0`aV‚e©6IO$¤a–`J£€^¡ÓAX6öôÒŒM‡ Œ+ã¥dLÙR’Ù²[,¼Ô±›2evgrx³VE0`õQÇhc¶ögYföΔ:™]ë‰Ö©—ZRðz ‹LiM+ÙÂ3„‰‰w-Í ãÔ/®Ñ‰Ä!+)’e#,òôö ñ8à:H“a ë„<>VàÔ-gnK`lErùÔö×¥ó}™ÄŽí`G½+½è…PèP}R9‚AsI¦Ðî±¢`TiBƒfèÖ9³h'VÙåâ˜m‘#,@ ‡ ”ΆEÁ¶ÁÝÜÚ3#/¦Læ3ÈdçJ×¼ÑLMmH) #/¦á#H. ÑUV8(|WAÈ]G’DX°j(L Œ°:ˆÄõñÉ#,ß±ouš)‚&ßZ7-½m^¶¯%â-OnÛÒL6C„CŒŽÛ…o4ÌXn74§ž"£¯˜Ùä/ë¼ô|…‘æ#-¤J:?åike$‘#3 œ9ð3 £·}hELȆŒ÷ Ç ˜pqD` ¡‡­#/ŒAĶ‘@Š÷ǘ†ØhñÛ²M ÐQ£f&—¬GcrÕV ÒÅÚ•HˆurŽ˜U1Êò.IÀ8ɽÌ0(vî)7†Fk6Â&Â¥$,Ù3£™‰HB*Ùˆ@…À†Çc‘¾Œ8pv¨Ú‚‚x±£Iè,Òóa½Íf‘л/Y‘Øï%Óz`X†‰FÐÁÍuC­T¡k@º ,Ä׫zè6‚/#5úì”&i°ÜŒ`èpWî¬5M‡4f3•‰cžm,OqÞÁ™º5¡·#;pd0Ãr›\A¹Ë„á­Ê¡‰ªJ,&0  "BÁþÆ @‡ø¶”ôBE„M )˜»‰h °RÂìJU#5#/†ŸÞñWòx¿«Õ;€ÚüwV춌Œ‚²­M!¸>cûÌ‚N´TzÊqˆÒ—B«øÀï•#A¢¨³°µ¡²Ó!!ÜApVà^È;U îËKùEòù‡ÓÜÌj4ÈÉëÇÖ31®[>”çq9"ë_óADpX³[dú¡Ç.¦Ð‹¤ ih§¤#5tÄݺ…±RÍŽAiÅÎdÎ-+X¸#‘ƒ†Ûd¦¦k`(o Í[FFK¦ Œ#5ÚïMݧ`b̸«;¡b¨mMšv*†|.ã©´¶Œ÷À8›ˆKœd¹B‘$1ÜýÚСU{nÝ:Çk–emúi-m&ó@„U Œ‘›ïò‚PØ8„C‹ —§®‰€—#/yPåJ–,‚Ъ1Ã:z÷ E¸ª[géý?P±;÷ÇÊq:‡—à ²¯ÒE]¿vÓM+oßkè¼l¼%ɦcI]wfsu‰QmÒþÑy©ü÷#mg¢}Ƨ¦p¡Ô²¤g,rHȳÚÐ"Q#/Fžãò±2Ð>Uñø”Ýq Bá0®#/7>ÓGÞ‡ˆÈa$¶ÅRQ´¦Ú+FÕ%E-35PhÛEŒ#/ €I#, U¯ø&ïaážY‚_h –êSß·«½qLCà]NØ*~M¦½ÂžéØ˦Ÿ¹ç|ÞÉ}9š5v ²KÂÆÙ‰#Mè h$ÝFY•D…¤Ò ro!¬¨{§ŸM@H Šqö:-'„Íe¬ #ÎfÐu”/V*(~ÆRH)#,ÔÉRZ üä;¶#,÷v6hñçÑS¬Xd&A·ßUÆh*1’Ó"F¿#tJ²4&Ði©²#,\ŠÒ%¢ Ä«ÅmXÔ¸óD1–!p„HO“š3pÃLä\ÅÍeº&šEKÀ1€‰dÀfwùÏGÁðbvúÊ#/‚›”]! BÑjÅ l].t§Øè$ Ýq.´ýÄ[YD*"Ix¢ü,†ØŠä(£¹œFXÿB`>šùdé0òð°A |ûÅA¼$&œ ?mFƆ´Â~Ø#¥Ço´] 35o]ÈŠÈæºü!®Ï½ý ñk@nmpF_ol==1–„ɶW¤3ˆyQ„«Kc+ChãÚ la¦oZ4´cX±EzŸÝì°ó:ìҙǥ éVP½´[MÚé×3.º˜)d«Cƒaç¬074ê¥#53©#3“Îðãžfi™ª¿Â&©¨ý;dÎѱÎù!ÝÙÇ6Ü=ˆ.Ù¢“•@°.I›9Ä`5†#/#`†k”¢eL¬;C¬€Ú ÝÒ–S3:g"K`¹ AÍ=:j4Y%¸ºv~Øg3ˆj ‚qÚv†Ì6K:A‰F%‘–XÄ)¥rÔ)ÀÁ\Ød‰ #,ÅÌT°Ò¸0¦L1 ¦ ™C#/‹  †9‡Z$#5YEÇ„03#/Žnh/Ë[棈 ”¸?ã RD^„NFÝáeÓ×Ýë± ³Ùeýjæ°pH±]” £ý'ófn ?Q?T¡pü¡Î °Ÿ»â HÐ5à$éLò°]YG=#,‘Z@ùýPÍ)¥^î d#,ÓNqN˜U4QUUU#5¨4ïFßÛH²[h­’­bÕ¤«Uùµ”Û˜Æ~+ëÄÈ’8}üϸïd­¾h‹s#5}Ë#5ó”‹PꦊüOô`8ã¿›Ü:ubÅr¿½†âa©k²wŽËGº#/Š«d‹‘A“‡,ë§ q¤Ó2enjêˆMªt”’ÂÕÊu…®iubãê;ÞÚÊEF1¯p1¶×Ðèà\PàÄiÊtó©·¡ÁªR ‰1œ¤¥Ö;KÌ4›y¦óYÛ¢ÜÌZ’ž‡¾:Ý­t Ù&)§Už pQßPYbÞÈ…13±³Ë5B"òQ(ÜD е±fKñ-g)\áÎ:;X´3ßääáœBàQ‘2qño#"ì°˜±J|Å!¢ž†”ֵ̕+â;vÕ™§{o(Q4Þø½,m½MŒ/ˆêtQõ6ûe)Ég‰žL˜â}¶`òdCRŽ MNä2Jò¢ƒc`”VwvÍfÜË—\”ä† ¾,,Cˆî— "( ÌÅ9xokeAE¦ #,ƒPJ`Æ ˜¬FŒÍ2<Âå݈ Jte.%¡qžûæÅZóȹ4Rë&¶nÈĉ@K™…œBèÒ…8€Ð…úÙ¦#5:z¶ˆŒ©zÙŸÒ55 ø]ªîÝ’m[}Fù.Kµ³awsK·\f©Î·³s¦]ÎÉÆN»Œ¶ò‰ªéh6ëWi+r»®Ýݤů;¥5;Ow^o5¼jB×-¹­ÚTj“Ch*‘Êëi#/#/ii"Vª†…#,nÛ^62šÓI I‘6V6ÆÔÔ¢™®¥®•¥šX‰¬1b«"CÑå´ÙÀ0Æ fl#/’+[IQY’$AG‰?I«Ñ{%´"Й ö1‰±Œ˜‡}’Ã`¤¨œNM£.Ä¢-"°T†Hl²o‚ób ê;Ï;‰ØL'¬„Ï^‡êaKîZˆYLÃ?y·òb<´@÷Äà#/NíÍtFLJçmz¢×«å¾=›ÏT¯¤Kóéƒy#,ú¨UEÆz,qƒ‚æPî ‡ö¼Ïé’Hð|{†éLûK‚‡8Ú~šêÙL/B`ÁBÑjÔ‚ªÿ~ #5ÃlƒBfh •Z4XRÒ#/BD×d7Q¶Ûfv[MÅ)”a‚Ñû³3ߥ‹}Ù9-ù·XùÄ6kÚP%,jTŽ5\nƒ È3§vŠ'ݬJ¢s\mje2+çÅ@̪3p—¿ zϱ•¹êkÐ#ƒ§l‚Ó r*°+xºg"De“§çvJ9ó“Ýg ë‘t/Ð…ÂÌ(7$ÑtãxŸ§èk¨Ó¡®{ƤØkTcEÚnÍLRY&mÞfè·­Œ‡êøvüc-LªÔ„µrœä¦”Š(–·Ÿ|›[[å­ Þºf²Ò¢Y¦›i›El¤ÕJûKü?ÉÛ_¦Õ_&©¬Óm”µ¾­‘#|)‚D…š– gv¼™J×JƶrÅQm‚”o ÀÄ#/Ôˆ™ÐYÙ5ÜP£ÕH%A@$E 5Å-%&Õñiº0”jIk)¶­6ͦV̵šSE*KbÃ#ELª•±-¥0”PÚl¥2%I›)™´B˜Õ2‰)(¶5&²T•¨5)”Y šJjE(™±%©i²6jɵ4˜,‘T¦IY°•ZPl¥H”$’e’“)3’jdÑK6©¶1ª&Vƒ"FÔ”Ô¦Û*šÖ¥šL–4¦MJUK*Ù*Õ|ªµÝ­²²ÛM¤³i)*ùM­®•6mT¦Ûi*­«á«Í–¢«ÌÕcZÑl¦ØßZªåj‘5jUP‘CIF9Ÿ/HpnÏÀ–NÝoŽ†MYÛH÷e³{°Ù2í›0ñÑcBÝë'~«ÅÈ㸫 ©½»hYá$©=!â^#/)ÂÈÞD‡u/÷€1ؾèž_n'¢£²=PÂÇk´4#5.Ã,=ü½,k³g]¸bÙ 3meÌzË£»>™2Aó1ŽË`yd„E,Žàuõ¥¯¡}8þMCFßl¬ët+÷ŒYE„ Ã¥Q¦(fI QÖšÃM$DD‚jXªÅU#/ û#,;£Ü{dkù‹X'©Üè,š¡é5vî‡K°6Á0ùå&¹Ó’ v¢ª~#,© åžÅåûÐÀƒ1›°ý"|u‚E. y!ŒJ’Š­ÝÓ¯ÀºÆ2âCÛstæ¦!\ \£:q±1®"µàv_´• åïB*75¶0"t^+òA¦#/tøySyjzM‡I!Š“»4ê#ñôÇåo{ºòœÆa^E\^i­éY×O<íF‡ëu¼]ðи±FµÝ qWX¼4×Ñ沶ð·eÍ’¶½ºQW]ƒÙkÏc“B7kéR“ŽæÇ1…qPë6Mù«!ÎÿÌ]"LÅÃþŽHe™EÆZP+®ŒHŒ–Ú¹ÛéÆýƒ’cZPPRJA7+¯è—'±Š+ÚN…7HI!„ØòX;A¡–5g—ZÞÄXí=òª4ÍáGÄÔAFM#/½P*‘*0f=EîdÑhduç§ej®C2)Ò¬„BáPª °aAär+ò0€¢Ld^±zî.5£'[šÑ3D#5GÇx?!Ó¥ãQT­9ÖM°)¥hw)¹_cí#PEêzq–§MKCr¦ UK†QË4`ÅŽŽL·~%cÞPt#"#,ȆÈHÈýËiTÐëlñ¬ZŤŊ ”a&B[e¯Õ›2FŽìe$qJLØù ¯ÅÞöÌh”È#/"#,#¤Òløb#5&’G­*ä‹áÌTcÄÐhŸ,ô­†Þ-:Õ uåÕkQ5; 墬lí4QYTQ˜×¶˜Plv;¼ò´wÇy#/ÔcSfW<|ò:Òx½‚›PìàÓ·rì·NñA4ÒÀb²’tÁG­ R]Sc#Ng#>Ìðn,•D ´é‰B!²L8 ¡Írº$Š¢Jª¢„IÖ¥ ˆ[J¨ÝØY’k¦ëmGÉm½È¶+jòïY>!S®@‰Œ`F‚eAB¢*\¤–ÙB#µÄÄÌ0€}”Û×–Å׃Æd>É·#€ª1@ð3;õxÎ]Ü©q¦¸@åÝÑ"Zº#4Å–ŠW‹‰·ë_x ,À¥™ùúùùBkdÂ+õµ¢šâÿ!Õ€E‡:“A#/b`5|ܤ€(BÙ1UmµrÆͲÍ%6W]6‰×M5ŠÔj¿‘XŠ,I¶kÚQ­½÷«Ùl©hÄú?ª­#,a#,APÂ"PÆ7À°Î!Bå„îa”ü1NaÖ3÷¬ÝBÓçUk´0+Ø"A¡0¬ðf·×6˜Ä¿&®/†``i4‡ßø` `¢ì3›c°0-¡€*ŸŸÐmÃ#,ŒAV¢ª4JÑàÉg?+ŸUú›4ð;㺣,7–"’IÅ ˆψ¹ª‡×>;vI#,%ÖCh#/ý¹;Ç‘éO«Yöýyñ½Ïí½Ï镯 ­vǺ.øuQ³“‰Ûì!ø«6åF†n²àCw7i¨ÎD=wyG˜¢"»ØT"Ûöqô#¬pvv- @¡Y^F#Kô¼j(Ûh b¯XTe[1nŒX„ š¥ßìöoçÈ:Á2 B?PC¨Šq#/ªeÂÕ؃ÏÑ騱â¯Ó=ì_è0[ öúÄ=CmVKb¶¿Rjµ,®mu›U·Kj£i+¬µ¹ZÚ5²ú°ïFŘ¢‚TD$A¢% !&#,3«=wÞÂîËÑýpAW¹ÁVØÀ¹JPe$À" ê%#59#ª:BaXLÚhcAèÐÚÙw°»5 ÈÅ3a OÖsarz“Í(Ñê É8š£ÊÎ%cþQ:øÁGÁU,!ÜHwÑÞ< 1÷¤µH²ØR**+àvúÐÜQø÷hc10*Ä5Ã…hÂômXp[,‡šûä€&¡#/ÇŸq‘ãßM¼+1yåÖ~N,HVRª¡¤Àhxü½ü;;R#¨” 8J<ˆ¨R6USK‹Ç^4R †ÃÓ~¼Ÿ}ì_Œþ=!…„P6QúÉÊ&÷-š[­T&–"`åÕ¼Sh ‡ 1³ØW6¼s«¶î‰(Ù5&*RVѵ Ô×Ê[´i­¦•”öWRk%XªQÛ°-î¹@mßß¿p¥Á;#i“ïvÒEÇ€42£tñJ¶éÆ­…;ˆy³ÌoTŠU7ûmiàУ6ˆøêU&$¨gmÍ(¥8¸àðÝ`ÎqDUå‡Äa3œêèHzmW¹"Á(ª ¢ÅQ!Æêˆ#&Rä+YO’@Áa¸è ]æ’ Ø 1G¹wù '¥íí`u’$’LÀýq±ƒƒíõë¿×÷[ÝÅ é¤ÐqØ%³™¿wRh„"…›hÂ)V›¢`¿äøm#/¹›dŽzÈþzÌö‹ž–¶ün´h5õâR—¦È¬‰Cá :'ª¥t›ûŒÂQPnÊv@às’¾7i5$Ÿ{1#,Ø(2š0Ù†A“ ˆ†#/Ü …F%#,>Ìõý¸ñŸf“Õšà0†n(c‚<ù˜1ЫÌÒrÏõ¢#,¨å¤Ñ*M—ÙóTI%FÁÄ8¶Y0z¦$öi…“¾ñ“òÄt¿Š6ÒuuóObˆ{_zišó®˜‚|ºNµÄ&åßMå~hÖˆ´b-"„¡jM$%"’ªm´µ%¢Ú21™¯Ö› 2hJ‹K?q¶¹µ¦eR¥%XÔQM)i6ÓLKfµJÆ›YM-dÖ‰±ØÚPj•£"ËZQ¦U-*kU’)h5†‡æz”#/i„O÷?6“Žìd¶¤"f#€ƒˆ£ÕáücD‚5$AJ ÍZ£j5¨ÚêkZånÍà[2@$dH¸buC»«~H_r«o³¯£[š¹ÃW(ÆMÔ¿Áꄉù’ ² ùË”Íü¬¯¯uV,š”#/¤K>av\öýÙ#,2Ï~ñ¡: ·(Û°»`ˆÜ§.H€w`‰åßþw0âW„Ah€ÔöDáÏMÂzÓŠÙýÄ A2=‹öP•U*P” •¨ßäQ/j[¤(q XO½œ¯"ØÊ<Þ¿ §ðÌ•´Üm×Bƒ$€ÑÕÐ!=L=|î€âª¥Êh"jD£9–8˜'ëÝ•Å¿2Ð33²›–7ü}‚§¨‰Ú½&Œ$ziDk´ç*©°å4ø †õ@<{GÈ#/gA'1#,bjIÄ·Xj‡P3väž<䱬äN Hw0ãÈàlúḆ©ü\£Ï\»(ÍØH±’ƒŒÄ¡ýk<ÆÍœ„!pIª0Zà~¿ÙgÊ!G¹Ëú «÷Çòùi‹|@¡4´²/âçÝü¤û»My–Ôl&žä"Nçký’À½[FYD0uűŒ[ªd“Bd˜bû©e²¶ö¹Ò^^à07 š…ÅsC’»+Ž_°ÞäÂ} ‡±‡±»­S .Ü6‹i@11Å¥²„øf£&K>^&—×m5ŸFá~¬RE ‘AN䥂(Æ#b„ñKw>ïdŸ«6kG´:htt#,á=_ïÝŒ—¡p2€É¼n¬²ÈH‚‚£c¦TÛ=Ò1ˆÈ,¶’2&š,K¨QC£Ou†n–ê–‹4T$Y”²‚`UD)å-SÆ“¢~„˼S.›€(ÐÛ XcQ“¬qÀ-Ô' ùŠ÷ÁˆÌòAydfe@v`b qLqºß m]*H™ªJêä€Jd¹Ý¡-îÁçtõ¨vÁwÄ"Á\Ý++î6-¿KÍyñòåÕ7<“yrBóÎ¥~I#k,Œ'¢½>ÝÍ•E“\‘P3HoVžLÆ¢"3°ÇwkM´×m*o#/¡'EÚÖÆÆÒq8‹¹‹A-ÂH0„¤”z ]á£ïÒÖ ¤,€ÓŒQU¦¦¼ÌœÅ<eÍŸûB'0rwd§ëµ¸Žjá*Çy²K5^P¸Ç @œ3)ÙïcÞ·Ôº¨WŠ…RÅa0ý¸;§ÃaDȢ߅¶•Êoïó£}g%ø×Ã.³Ì¡T ÔåÊBIª¤‹n¼†È[FRPì#·[Ñ$Y íTiãŠ$økÒ¢µùfúÄ/êvAÎW<r²àˆ›®G$ÆbBÅÞì‰LCh~µ–Å…'6ÌA5F#,Öýäˆ7¤¥‰Oß¼(ÀF[$ï&»ÂZJËÜåóÞöcn½ŒBÛÁª?³ja䥅‹J“k.91-”âa¬†x}L®³Q·#/£ìfξž@¶k€jGR‚†‚РˆÙ±¼º]ª…`B@Fì(‰•iR´@HôE(¨„Ážf´#5¤•Àõw”:S §™±tEvoöο±P1uã±Ä5ç鬙kÈb¤ÄD(u’´ÇvËæð9´t` ¿«ØÜxÄD$6!É›UdE:ö`VAA%Z¬KÆö¶‹¸\ˆÙGèˆeêÉp×^Ó•³j‰©Èàýý°Öúü›Ý<‘}]ÜjÄ~ñhóuµ]v›xPšDÛ^ºò¥ƒQZ›JW 6ìB #,Æ„6’% 1‘¡(2¤*TUF¡ (PH}WC™È¢‚Ø÷P;{kTƒ¾ ˆ‹`©º›SU(ª¨€ªûù^§òW{Ä1\*ð&GaWŽ0­ n(šwŸ##57ôx—nïmëĬcºÂ[p™Œ(À#5Óa;@#:÷/2Þ(’µåÎæ“_R(ô€Ç¦9»ZÉ6ÐIN0ŒJlC*šDh) ›Ñ6¸L±"l˜sEg6f%Y¨–Sd WLë ÆžB]å0ìè›È‰Û­«DÈÂHÙ“Ä/òmZhl0Ò#/ós`Ö¤UD7wD 0ÚÄ1l®¤2Ž{TõEOé8 ÚpãÂB@„u"T#,/˜-ÙÖ—77¡çKïîcI©Ûê´Œ#d=^£š‘ix=KZ…ì#/À›'ùõ6á­•F¿sƒLu8š&ÇÀ:hì¡)#,êƒy, ‡¢*]ZRj™¶ÊZ6eLÖ¨ªRµùUù›[ò"W]¶Ú¹±…!(`°¤)’(£Î€‡Oݸcó!Ò<®Yh¹·£j˜_HqÚ#5€¬==Ö³VB¡ïš1NGŽÂ^uç–\T[”žö«öÑäÝYGÞAÏ ŒC#»:8ŒmNçÒãÓYÂÉŸ÷`â/ $–”kR/ë~ªÀÚE–y'áPÉ<Ö»ùf·˜ÀÚ4Ñ$ØÔÙ'½å2‰¡Ž’Êhå‡8á}“n±ªÓl@‹ŒQ:ÉP#5FõQA4ä'^ Ã#5 1ÉW)«¬ï½ºôë1LÅ‹=•_ ²¦))Š•*l#/r(\4JM¦f†AæwôÒþY’Kjé>Æ#/­µø˜þÜ‘cVÏN}M·ø¦<óMÛò#gz8ò!§Þì2vu¬#/eÁ;|jù`³ßÛDF`(ƒj¿ÕD«–™÷yyž#,zÐOIâ¢Û€Qž”`4jû{~Û‡œ^È”˜„ãÞ…ð dÅ(›N©È”ÕúñtHQ|ÑÑ÷&4Í@{R!ÄÇ*›ÑÀ܈7,cŒ —!Ïë`²|èVcSWn;>³<D’×—ê½õÓÖã„nŽ™àO‡d¿`”:üg„3!š¶#ªTÝ%T@÷DgD47:ÍŸvÁ£ Õ^}ó¯rû žŒGœ·1Ç+>ÇiïrC€Â'*¥YC(•9$—š 2idHAˆ½-"g7{´¿=Ù€¡¸/Bï6.®¯W°°³ý_EÞ+ Q!¶yVÑ`¨ÄÀdvÊ—RÚÕžvëÓ½©«lÍ[z­t֯ћSM_©uҕĈàVxâùÈ›ÍFFRàs±Ó$h1jšø$”¯ü_íbÇõ7ñ¶îÒÙè’¤=È?ìî› ƒÇ•™hæžrõ[F*&ƒkã±xŠ©ÄójV°¸²¡#Ê~HRe¬ç­ulLôеd#/0÷ƒ4â…ÅÁ±å­­Io#|Nz>P 4Ç–" ˆ§R7ø†%C2ämê﹄€G¢#`Â#5í‚H_8FHT¤Õ¨,±\ÝÂØJ€ ·vµ×,ÆÝn³Viµ›6ÔÍi’¥™«lËUu[7e4¼\“Jím}R¦bÚÙ`@E9Øèq`@ß: ë÷ÏDéÏÙñ»oUëÖæ÷<˜ÜÔ³ßÏØÒÙ5DzÌ"µäú*‘‹4„ª‡ÎÓ5!ñ>éMI¹ó†3lÂAëõlõš¾…ÒcZ<«ëÑÄ#/›rÄ£I-¥¦‹ý{¨l­>@ ÈÁkQ ²LU‡MFa°õ8½Ö¬ìbE,…” Æ4ú#/Sž¿Õ¹õ©ßW#/6ÞM»J16æ¸ß¸7plëÁð#/ÈâÊÅ_p6h^²oó®Ãnªø‚,ESøëÒ`ØßÕí[¸`”]Ô¶©#5hŽXæUÕ»°¨Dˆ€A¶ÄÂó RF~ÃVø‚Z*îÖÈ3%Ø÷Hk)JòFÛÐlÈ(3™TqMP90yÍEÁšAuög¡å#/­áTCð~”z,^G™"`÷¿=·<â‘I:~ÇÁï4òOZiƒ[9 ‰PÜäÈù}þg›òÙI0IÐFÿˆK÷#tH²À¬í dZvζâŒî0\7 ŠwnòÐ;#/ì8…‹È® Älô Ò¬ÜÖp#/ÇØ\×yܽ÷ù˜Œ"0ð3Ázõ¶ÛñâiK¸™íÚ(ˆ—IˆöÀ²RaDú•VIu }YK=™.!ö»Ò< áàE„Ö0‚È'!ÔeÁÝ)þ5‚›«1ŽÕÜkÂô Ž¡È3oÅ0ƒ? T7å àÜ„Aú tæ„&Øðº4qü`e3bXFËM£Lö›-¿#/ždl‰ß‚…@rMÓÔ±î£?Â27!áSÑ°»SXÇ•‚´ºÛ¬ÐÊR5vûÃÌø Š‡ÇßÊ|7ÓÜR~"Ä@ž €¨‰™ç;§2€^|7Á2ªº€¡êȵx«Qm¶Š¢¶Å¶£jŒQ!FÒ HP4&0»æû_ò__l'¬a€Ä;HÀ‰@?¿¡Á¹U{ ¸ØŽèØtÛO®¶qqFåœÃØ(nao3[²AD†¬Ì¹ë"1è2•Äšž—-¶ä+|±ÇšpAÌŸî#,í¦ž§˜`8óâŒ!o#˜$†Q[4ÆycP!øÖX¦20üÝ¡ðµ æú4ñý_­óU Q¨(¦‡­O^ýÔª›ÍÔ”IÐ(9†<„£ÄÏB€£`0Pž"ÂdvIOè!·ð©#/[IÀÔ£×ÇyÕ3†Ó}„ˆþ3ÍBçÕÜ|Fo«1=ð<6}Õ æeÖŽž˜Òbè T#/°*ãUÊJª’ƒÉïÁo6Í÷M¾¾å®Å]Ò«!²~Ô¡ˆÀˆ’{ÏaM² dp£AlMH?¿Ýî.xS>Cŧ£1îM|,‘¦Ýøòëd50®n>0†3¿hkľEƒÝFo5qIE.8<<4‰fjiªž ‹4ÍéßcÛC½U³5üïÂÙ”.!CF^ž¢2ónN£méEvÏ>†ùa0cc…¦ó±±—½æ¾¦ºèmi`Q00ƒ!’Ÿ€O¸eSiA›Léõ]-ÊÓ#âý%í»ìç/F 8éØ]T(£¾¬I¼¾ Úñƒ¼ç”ý8.0üa­áæy•žExѭퟧfpuꛧ<‘#5òáÃáŒÔ¤–%áq y³+Òž¤ÒœcÅQMBBÈniúÅI¼·Å‚ø¡d8ΓÙ#5ß<†×ÙÀ®Ù0áH:í@i}᤿5K`S™Ó#/$¦S«ø}aêêü“S.=êqP@o’ƒæ "¨ÌˆØZK‹d²¿8("|hUlºCHMœq­À°¦«2£J±¥ Á.å¾VñÒɼm»*®·6²m»ÇI±Z*òêK][r®íÈÌÝ×h®®ifÍÈÙ±²¥¹Â§vÖ’ÚM³l¬Œ#,´…ˆ BAB¨2ìÀl@¤œPK°ú†G ˆ­;ÑȨÛ£Ó)¡ñ¡Jk©ôaùuó÷¾vÖùµ`J•l¾5YpMZÒ”ºÕ»-¶ZóËlþ8¶P¸7Š‹Â6XjbH2@'%ÍÊÍ|my3D¶[kÊÕÑF.>/=òe5ŠÂ) €Ê„Ë!E´Vñ«©•I? íP%±X2”ض-¢1Y•Æ£TmTÍ££EE3F²Ve$ÈÍR Å´Í%±kf¯vè#5$9 ”A´÷ï°¡òÄÅ:þ,€ÉôÖ­{{#/&Õ„ “ky·í_‚ØúW§|vñm#/O:ˆ´OãÅÍ·’r½Š£]³„×VÃÑP"å#,‰H@Œ“¶rñMš*¯É+~&µ\JÚñ[¥ý[v"V)O7]&©ÝÕÓk·i)µ®&Åm£&²Vî®Ò›V•T¯W[KIQ‚”‹MÿÍ!Ä6^Ÿ™ A~’ŒüjIèJU G&0A´É F;JCay8ä X„EV)!­ÛP\7œøµJW#5#/ÅxaNŠ¦èŒ"ïW]0äÂñÕmúŸhP{ö5>Âo>«ãÆO~#5?^‹HÃ)AˆÃðEƒÞ~ÁÖ•84}& ð¸ëmf,FE[ÃôÙTlcUáŒcÓJ€ÐÒÌ­Y… ö2º$šhÁŒŸÇŠ÷ånµ´ÍáöLc,ÇzÈð³†d·øš˜ÛÇ¿aCsb­†á‹»±-PZ—öR#5¥(P ¼!Ûgꮤ¸MŒ;:>‡$Q8duÃ<^ÑwᲩœù½‹§[¢5]ÉÖó™¢mÜY`¾‚[hÆ‚QŸg{«42Š¸Ó-½?¾ãø“¾â ˆ#ö§Ö;ûñáÀ¬föÖãu»`vH©½O*£•ÄQÃ÷žt?º±ë0T‡!‰çÜLì3Kܺn¨E2ñ-Åõ!ó`y†f@<`«ôMmcTm¡-¾º_}µ¯Ï-äŒHÚGüäL¯J&12ˆÈg#,KEdχQlšÕZ5\Õµåuæê½À•ÜX1ü€nËÇ>pD  <ê0?a?C_¡ÏÙXZQ¶AÕ&´×_·y®ùq¢ÚÊV®––0£%îh€Ö ƒBƒKF¡¾@ÁÛXÍ…£ô½šØ‰Pl4$†ƒÚ¶ïvÕ¾Ø5ª™IŒQ­UãM±oŸ¸Ú¼î݉ð»Æ®¤J(D¦eR{Œ%—!a#‘¤šZawiZ{Ò¥cl¬Â‚àL#5X»‰›¹*Äid-Y* «°ˆ f c €ÂŠ‰(T(±`4½¨t6ÀLÁƒï¥¤µ\E”C2èËôé½q4bàÓ( ¢1‡¸–T„ ò_IP¼æð…#/JK¯¬úpcÒ4a`ªsõ€q•cõfLÜ&†CsÎÎWРèqŒc0Ô–£nÜþòÝš3³˜@9@’zDTQ1#,ò2“v¶ßIùüihqºX=nìšG·o¼7¢Éÿ¶¤²ÂçägƒgHÏíUyì78~X‡C8GG×!HC‘{zðç±úµ:Ô\9úpXÉŸWó𶖬¬äôþ1] ½þ¤q{8ë©qö8é ÖË(΀TáÇPUAà vü›&—ɷ䘎YJè’¤ÓHB eŠ#ÜÄI™˜ŸxÐ×mí Ç2¸¥©#5?U€yÈ,ª)ji-: [G„0÷KÁlÅ@(`-ËYEl8¶<¾€ÂàWH#“‘^yÎéáÏ>Â;u茒ÿi~6a“éã!ØgtÎÇ…ô+ÁæQÃË®6õ4õ5AÝþ÷@>þ¸’AðEUø&+»tî•\Õ¦·[MJ¾ü!VùîwÐP…D¨Þw&Vãû3‹u÷6Ûè×\J•Õ°fÓ±ˆF{‚Í…wŸ£Ù¸’I Ž-U#ÎòóÍ4櫆Iºï<Þe¢S–æ^s JjI’±”ªñmÕ+bÑ ÉcA²[Âk¦´š;¥uy¼GK¶è\·wn‘]/ñµx£ššW——mvœ¶e’uÏ5·U±«m±³MO5Ú“&Ôi6žuÜY.»­Ý”é·E+;®×*êNîÓbÑcš¨¦†(TE‡÷P(ÆÂ)qUö'²pØ`›‚‹Ù·×½UƘ[µ9QüžŠ$;ÔþÞñ-F :Eð„ €H–@S¹ˆ–U—Ú£ä$ëÄËóGÞŠÃx Ø×H7#,|`HŒLÃkÑÒûÙø¦‹õè•ÅÝ°ŒŠÐîõÞš7zòñŸu£á¹?GsÔ›NÀõ‘t•XÖfmQ)™¥ôÕíÌ$ è„#5uZ*Ðb Áp?„#,X(g:½ül*&ŒXEXD’0I†$OaáêPúÕBùòèØ5+¶u×wRÍEE»­w·m¼’ü>ï!ªgá#5ÂØÕ5jL¢·‹ÃÚR¨g ¢ÔA¢έðˆR&HÁz×M!;ŠH¢@0·1ÃB"ŠÌf£o¾uªˆÖ­×m»i³ìkÊŸë«nÕ»Ål¢&4:§žýé˜d'È@k:dÿGöáí)R0 L0)A´(‚#5l`|ÝüSCÙXÔ ú€î@#/ñ¦¶›YfËU4Ö¦ØÛi©ƒ†ð/#,1Û³é0VçQ#,uÌmõí³lšÊ¢¥Ô™›` *ÏÈñ`ÔÕT#¸ú0ÑFÑJRRj˜0²”€'ðP"$P ¹pÃrª¿êƒŽs[S>Šã5‹±\”å,AL•¦~>4Â7H΄ˆx²gW‹Llf-mEúºw÷c~«#,¥¼‘Šb„ BOÄ!{ßá?=¹^"Eå#,úe‡0ƒù³È8ƒÈg„+`_ÓÕ}CyfÊÈHC ÛIÚÔ4ˆ>7€ ÔÀÚz“8blfh J å…¡ü #>,-j#ø6 `j#/$Õ’1W&³hÂÁ8é$B(rA#/…Ú²¨0@ÞÝEd&1<¬¨#5´b#N°ÑM›¨X; •AµM¤‰„Xñ`]ÛL¨žúów•æ·ˆÖ-GŠ®X¼jå“W/‹*µâ¶«ÆW¬5úR¶Ûk;fN=Ñó{njöÖö‰0Ã#h~€bH$ÑG(‹"c\Æ<.ð ²Hk_Æ*-j‹Fž›i¡µ,Œ%£!Q*Ã6ÅîTb¨ÄIE#,ª5AE@‘‰w@ALZ`m6Ô]ƒÆC±Ï<±ÀHA}~…Ýɨ'§bWŒÅå½ù}$µ…ª ”Ž!A¤õ@Œ%(”Р7#/€º±—|ò³±†äOvj)À‘Œ$° ÄyDz·A¢ ²©€D‰(…B¨ ó÷E>A×óÕëX4¯ž6Y7OmwM¢ f#/”bubF(´„ÌèyCó?4YÐ5™á3˜Ag™OøÅ6¼zôþ¥üÔÎàÆ–ûlvAÈ[Ú>pZJ¥¤ÖðÀçÙ*üÍm5ÚFv“°ÃC$MËUÚèˆñt»¼îõÖë‰ùHÓF Á0cKOxÔÌnÉ©]!ýšp#/2ŒÃZ}G‡ý%R¨,¤³N ¢2M«O‘BPZÿ L–ڽϰÜtvc¦#5vbz"‰ù'‘êÔ/#/¦gyêA¼™Â ¯1>¸ìFâˆTWlüÕAœv÷ª ¹FEÆ"€HRD'º†˜ˆ¬¢#,‘#,“˜C¼"XSò#5â`yÓ`ì~ø#…¾Þ¨$ ¼Bﺌí§Pðþ>ödu½ê`B#’4”„§è¶¿nU°››¢ ’‚'ÐoÔ‡®Bx|úŸ¨WÓ—Jö•À."Š5'á±½}å™ÍP9JH”(˜ oÓì#MøÍõ-›¨6œh–Ã%wúÌ°:wýf79 ÕÓ¤’•*ø˜“c¥L°º†Ä@ûÐg¡Ð¢‡\ææiÆ`Ïz\·^è!ö!£[$f”§4XBKš LÈ­*­`&Y4n"Z}_$?/‚x*»ý»Û–&ào÷#/¼h‡¼Hba !ðd;¬_/Q­}h7Êúæ "/:ÜÏxð±Ö/Ë×ÆmàÛ™UT`ûäsž“»¼Ñ6]Y3PµŸÍ{4ÂÖšüÍ—èª;VÊBÝE‹–e0à:ûv„r¨Ã`Ð4#/'8eF©j¬qÛE¤¦‹„b±"À,q­Á#/#+Lé m-0f“Q2ÃìgrI §!Žú²Ù.¼æL;ëúµ HùƒNU”kbf CÕǯFsSÖçØ·÷R'¶ Kâv+Š`Ç”7±={9 lßGf-2>ú#¾Ýº‹xãˆZÞÂœ¡¨x‘ª†¯n¼ÇŸ‘ÞS¿eµI™Îé•|>Ÿõª·ò:lÊP#5Á.ƒÛ^)¥àIò z nÙp£¦ÓLMñéfÿÍß fæ‡ôl‚¥Þ5©^‡.ØÝJud„&ó#5!„3@8‡ )ùÔPhȘz#/QO@'»1ÔÃk:#/Âs>ø;ÁóÊõYZ”*€ðo†vfƒa‘UAÉðìêêÝž@JŸÏ8EuBåœs¦cÈÅš.oŽA°Mı Ô@i©Øp› n"õ¥R¡Û¯†VàeA‰Ð`T#š…pÿîöÔÀ¹@±~“K?¹’|v‘=•ýÞÐ0 Õ iL$DXªÝ–Y„´Œ` Ðò(ôêó®4 m¸¬žÝÍ#5hñ(>ú©¿Ñ1ðm§Ž#/?­üì9óœMI#5BHj¿ŸæÖÃ}_¹5}J#/~‡Ê­ÈÑ­¼šÒnD„ÄF‘ï’FrÙš»“…@ä7®ÇÍ<Ãó‡*„óÙµÉVÓee¿vÂvøxw$NIYúðñð›|h“"Øê½'¯dÐ<õCrÆXµÁ !߈h!2ocnÛìüÞZqpÂD¡ñ·=ØØ–ad1äm”" ¢#,™O2åkp´Î`ì ÒS¾ctà|]ËLõ¦ýW“„ 9Ñ1☖òFqexèô¤@îŽ?fëUÐÒáa¬';ôß¼tN*©&d8œ³ÇFÞ#/­bá=‚u³ž»YfŽ–¡˜ö:Ux1°ì¨HI$«6]ÇÖÂ0á|XBm™a¬Œ3¥|HM®JQ…8åÔæ,ãW7æï÷l—ÆŸÒÐ È9þ¹10H¤%¤"±Dô©ü‰¥a|> _éUƒw¸ˆŠ’H0ˆ²»KUÕ#/ŠŸ+›ägØ~Z}/¥0#/´Mê:·²1ƒÈ4=çÙ‡ä=éÙ.¢ý<ÍÌ4-à˜›Pr<ÎœD1Ûômz»¼Ì-¼¿jö¹ùäqò â admæC\ÕjÌ0¶ØÕ¬Õ­-K‡T\c#ç ŠÁ^%Š¤â#5šI#5–jwáx<ÖCí3ôƒhÇÕlº‰µ&W±Í-þž˜Å7AãÚo@Œ4œPCr‘†A)FP%DE!] þO¯*܆áĬé¼~žøÞÒ¿«·×½m™ Où¡TŠ´ÛÀ¤ê@>Í]`É÷*çÀt[6¸œpbZõõò`¡RÚ¡oB7‹#/]}Ùœf‰ <ŽF[CؘÀ€UUFëa…ß`}¦ì¥‘÷œ(–Æ®-Š ,HÀZÚÈ}x& ¢B·º²#/"PÔ³ÒäÃîË£©t3ìµ¢‘H";r÷ý{à~SH«ë¸î­ËmÜýû­RQ¬c[eRšVjlX–­*@B,êݲÀnxR…÷!ÄÄ ;òøô#5ŸKD‚R‘H(EÝ»ðå4Ó·ùo<ÀdRlÌÈËI&Â#/4”$¦Ä¡´ÊÃ%A²ÊšKHÓ"BÊ”©d)k÷s`Z#,Ç14t„ƒ¬GJf:Óc¡Îž©ËQ¦éá‡uáuž9Î.*v3#/&È„m1¨Œá˜A®¯ ´Ái¯%Q÷:¬Ë+Wwiå#Z¼tÃ+*qÝðif¤ž¸gè,LkzHÅâ ŠËÄ8öŽ \@ !ÞõóŸ)ãI³Ùœ¢»Bº@صYt+t'a¦˜¶@qÇ(£#”T‰¹ù&@b8˜ŽCq£â\Z.=ÛÏxÎ:oS,¢j¶†­õ¢²ö ߎ ÁÒägb5¥œÌ< 1wqáÇ&”¢òDd$‡(íõc_“#ã1ïQkR3„†ð×4V5ý ãÏóOíY;Ô1ZäI ¤þ4DF@ÂÕm¨¬‰–ÁºÆÄ#/24A#I`‚mŠ¶ CbH†Rg~¶®©\º«–›®Õ&·g%Í%æÊJ6Ć˜‰Å²´1¬™p.¶c6ÑŒ‹V#5ÀZ€;nR¡.Ñr!E’ŽË%#,æ8²¢ÚÔf?Ê#,*Â@c 3W¹“n].jw[¥¶Ûu{õ#/'·„ØÌÓšQŒAÌ<ë#,B™‘„#5ÅÓ#,{¨ãZm§ìU+qؼñA¬ ËÉXc)ÿ¼0Çðø›ÀÂB?'Yã ]5Ý]Ñ£È<ØüâWð/#5ŽÑ@Wjš2¥aeŽ(h¤b‚lt¬‡âÚlš(± ³\8l¨´›M ¢¼m¯smžæ-Ë·q‘Jr‰HÄÁ¸¢U =›y#iGM(ÚE¥ “š„qd ¾Dƒ,DCtF“X8¢‰Ë#QI_S³=Ok­{ïÃÂ%{Ž¸M- 6:7&Œ¡+…U§Kd2³ämbB¸ˆX°N5R+%"{2€±d+ 31ÆÒÆ6C ë ˜`ÃRtn(”¥#…IB=$yÔ*`ÝɳcšÖo(žjïzÝŠf;P˜›dÛ.ñæijæfyPŽ"eMž¡²X”™um[¸£ ‘Ƭ•¼#5Õds"X,#ËŒ£2†5€õˆß`ښÊÉïëñk†èh1ÐGC7DAŒj3l3 ™YÚ¨8‡¨Ñ#/j,Œ•Å‘ƒŒoÎàÃR féŒlñ­Y‚n(ë9·4TÖ€5#57× d.vt<Ô`¤yªŽŠ± á´¬f#/ôœ²j4œjŒ#/ ‚b¢Ý#5f“E³(È’Šɦ“bóLÛãÆMœcHB^»>hƒÛqËÙØÚ0‹£X>¥i °Nm(ÐvÖ­$äE‰l2ê’[BÜÁšf‰”,˜D„i4ÀND™…”’$119#,ƒ(`Pè…¤Œä¡¬GäV:Xi°Ç“Fµ}#,Œéáˆcm˜â¢6„˜&0‰ ‚hT‚"m#/,)^ª§FJ&ô{Xg&ÃAÏk´cG6»L~´c´¢¼A*u6(Ì&BÖ-#/4©ªcl[ÈËn“±1›g­M#m>Jash(Ë°Öšû¡ìbÂ…täDT×çѤpìEÐÄUHOID6<¥´ yÃpß4t=(·YeºU$â9¶H|“m'#,÷:hÜF [s(€Ùؘ;0fȈ„i-Æø*@Á’ä4!B†ÐQ bJŠ£"¤Á€4l`Ñö’È^)"¢à4fŸj´ÉÌéÜ‘iì%0±ý­*’ñ…*åM±“w½-åÝ›¤öñºë•ê¼^Mß]öWà´bƒkFª Qd`‘‹!GQ#5Õ?÷w#,YCüBd¼1#,.&ˆOϺýP6” $-Ù#,.vâ¢ê~Oæ2Æ÷¹E@_3#5#/(žª(ÔúˆBŠ2 ¨nÊH B"ˆÈ¢DŒ‹Fhƒ¸Š,™—±î B'¸¢´A9^çHä;æy{ìmò¡# BäR”Ÿ–Ÿ“Å!KFúÃyhÁ…ˆ7wRƒNçÖ‡uדù«xõÅœëcn®®š—¤ *©’Ásj‹G½%Ú¹™‚⋱‚†ótRSRHCö)gœ€víRK†æ¦ì·È˜Ê‰#/0ã§-™÷2cºþo£PDS{n)¥®åÙÞZƸK‘Þ¸k©ô¸–—à!ôÒ`6ùáw/hæÌŒGê]!¯Ú瘙Ií.À±œöÆ6AÈ¿x_Né‚!›Þq; þ°þH®­HD™¹§F*·;2“è×À “¿òyg©‰±!ê"^¸«gŸUݾÌõt48Y©/#/Ðý‡´HÆ"“L(òpDèE‘‚2%tù§`˜†Y‡¼ß{OGºÔwËRÃ*ãmTXÏêúçÖët©²å\T*#,›˜;Š5ÚkíÈ7–9”O.¢|ÂÀø`J¡Ù•[Y Ã¬Ã"ÜÎóh˜…²FbÁ"©TûY‚@ÀhZ¨ÑžæƒSé~'éãÅc«5nZUI'(i¶(°¦0%$Ù«âoƒ}Ì6À—r¾âçöåQB TÜ@´C!Uþ5A”ª¦t ÊKQ¶˜—VÅ×[ ÖOW4mðæ·_n›kžWbW¯®[kÇ5sh£xÜÖï;ZÅɵ!Ín•k`[%²BØ  KVØ#/ép¦Ýýæ·^ud®îå5\¯WÂ’,b#Hƒ¼aN@ð{4TÂØ8¦"|@º\GØ™ûƒÐb·²4c@IŠ¢å#,GÄŠÚ Ò#,z@÷?Ë€âEK´±ƒ»»omWÇ$ñ4§ƒ¾P¯Î!ÌSÎ)JÐ#—å?q @>úà…%Xù)Ô‰‚n"ÈUElïµ”×%­Ù¥®ãm×w#5ì¶tXîï.áB¶—T€§‚%€ÄÑ„pZÅj-¶´–¶íj&Úº„Y$#5j¤K6V#5˜vÉÄÂØ‘DNDQƒ$VHATˆ} #,Í#,t”¯ý¥Põ*†¿š£¥'U÷ÕOF¾áãA°GÞB&Çq–º¾ —ˆ#5©˜½¸³Àô¨!ÜzM‰X=ˆ`¿YÌÅéô”H'J^‡·`dÅï‚„:>B¹bEc„dR@²l¥14Øщ¡³-3DÊJI‰±´2¶ÑV¶Å¶ÅV›J¦U*Å©­cXÚf¢Í^hû‡Ÿik’2µ™¶@ Ð91‚•È‘>ü)pM#/1¨<¡–‘²1HEéd%T°5˜«ÊAh GGŒXÝ"˜W#5)`#/ƒeldE‘¡È1’T:„¨UFÁ‚44D"22*T ­Œ#,„•æfi–L"Ä'Ïõåkª&ô%¡ŠY¥¥Ý;7 ëO®׽^ùPD AF• B c#5FÖ‡h.*W N0é°^ËÅ6¦´“ÝshhyöKkÎ#/«EU)¥^ }#,1 ’´6ÃÃÛ±”!77´qÊà)…2u—ðm:;9øtM­`,Å#5ÎY!‹VF@A1ÄÞ©µ-Ö$<8k”bHµ›†µ’Âe.#,óm“Æááçç$¿Fb‰{¤£Žõç—]5ÕæÒåLÌßrý•ßÛ•F­¨ÛU\Õ\Ö»½¦©+ãkÝìÕÕààØç÷AÊ.qþY ”D³Úû°¹bVUÒã6±dš¤X|?f¸èmŽ8ÖAŒâ凔´}cï€r×BüÎÜâ'óÄË͸=÷PnÝO12R_áë'¸¯òvÛ¸‘´ÖâËϯ¿¶o.‘”‘ ï×eu¨={¬çpïÍl’×iõÜ“ÅÆ;ç:Ý7OKt Œk/ÑÂ)áÎ…-™Š—ÛÉ[ž/}‘äEð„/,L'ü©r‡B¤òŒ‹ôèþ©ºó’9·å]¸tMÐߊkåGW¨tYAÑ3Ï[”5ÑlJèÚ‡ÜEúy»F]Î×!Ý»®éŸÂ%#,âÅÃWN-¿b2 ÖtóßtU°uĶCŒ¯LÕK7ZXn®?“êv¹#,:°éÙN-Q“6ç¶<”h•Ò䮚ž¥°Ïå0¼‡99{ÃV48ŽŽÙ=4å²γÑ|=ÍÍvLç”ù·:8D§Èãwwj%™ ]N•æøkµŒa‚Ñnt{òƒu^9{’×F娄ǟ¥IÔ?9 ~\³½¶Í•Dã숭lÃ>7éŽÌʲéßá*ÔpDÝ5¿]NuD¤¨€ßTSLœEHˆE’æêßmyžú,[¯·ýxvvb–)ªÞWƒÕÛbOãdéb€ºL(«D”Ž—2ùÿS°wÈ´;¿®K×Oy7ËcÒ›c¢P4áÑ*“m…™¨W•Ï’’”ÇÊq[Û¾kÚwy@ûcoÍÑñð{ÃTã¢Ó<#1܇Ä÷Q'wbâü@ë¾½ìâ±ÊŽ¦u~HŠŠÉR×Þù*W˜çO•vé5Ó>ü¤#”F#5wÁTÀá öª'¤ÇRNÐú³†ºÐìÌ rldã5·îù<…ŽeÍëS’]žú=ÃñôÂ3«¦gÂH:&f#|8ßJÙjZœÊJÉÏËÇv0„! ³uŽ‹Šxéǘ!ÝÑÌ®ZœÕQ…sãšQ!Ô8s’„;È+Äu5LÆldR-ߧXRpËf‘(%¸t&!Hìj@"¢ˆÌkè/Nâ~’Ó#núž‰±gëFpðægž&˜Ý»áAÄÈéÐrúçü¾Õ´Ïq©6ºð?qä—™%bLn8ƒY|)®Ø¨ñ¬D¡ÙºøvKxdÍB—3¦zÛàMæ,4C^æ£ÜR£oä~‘¬—2_&O”O¼û˜¬àsÎÁÐç©7ï}ðkÄcŠvòÓ—#³ùônš8ü­øщH…Ë»<|:Ѫí`ƒ¬ºê‡ïD[¿e1›Ú\š»÷Æʤӱﳷ Üì.u«#ÑŸ^$àU†|£3ŸH%LôG›È´­$‰PúžÔªï¢sÇ,¤Ñlu;tm_8Ûð'äésâ]«1Çú`Ü©G„Aï‡#5ë?­å#­ðv(+t>Tú=õ®¾½<ö Aïäx @€2#5Õ‡¦æ¦&Gcª'!c–#/·Xäe“j•¦#/»•*Ô•©ò<óXœªG­_qû”4b8ì;z‹=AÀ~xHôj „qDÃ1#/*Î’v6˜±)‘){¬o¡xÑÇlè4gwò `¨äsÑ—ÅÏ•8Õ…•LbYˆØ-Á\/ILIUãœ)–Íd›mÀ9ÃmRº½Ã”(•‚YãJ^X*'¯$ÂõtÖþx“%S4!2Bð2Їs½le!ÒŠš #+‡—`“3‰—…ÚÊÂ\ºê!l2  ñ9’F£w»PW ¨ÞdÐÏa!HØ6Žã}i*ÔìÍ®“,Ìj]Š¹vmH+‘´¨óËnûd¥lrË-–¼œEŠ¡¹g2PÅj] Aò™–t#, )zè)É(— #5ˆF#,B(D[ðÄÑdÛ«…vmìdz²ÓÄ€îÐèRÏF‘,¨NHéÄÃ-òIÓ ®·@#/Ð3@Ë(ÈOœÔuç öÐÎ>± 9A%A‡š\=ZïëÅ?š•|4G°ª(="…LˆŽÈ»CÏ=Gš/¿ôK¾Ãßž<‡>7p%RÞh¥CÙL²s;Iî‡û‡H)Y.$gá„FÛ\ám°ØÌ>òùÓœ>O룦z“Ò»¢ãÕP{ƒ÷ó´‡M9ëµÉjj?Þ/£#/Û¿·k­¬„7U¨òƒ¹òwF‘®û³e1¶ˆÃw¿1¥6 X'#,Ä öJÇÄ·–Ûu¥¤kL9w*ûGnô |Pz iló0;®8~k¸óé¿F‹„ds$.j¦¸–S½£$2*!‘„’×S’Ú–3o`b‰#4@öæR÷àƒ ý8Dæ‚šyvvã˜b;8=qÀIzÓ¾.TéhünøÉÖáÆA¨Ò¦¼µØÖØq<3Gá¥:¸†ÀùŒ‘éÓM^áO•õÙK»p| 9#/š5ÞUš˜¡ª æË#5ïh*ÆN5²Qu}X5EG’ma¼°áÂÙeœ"5#/ƒ‹â¾—PAÍþüÆ8ýNœµ)é FT³†o—AÓ§\pµ Sh%¨ÔVÕ*ÉRXs›)ºÖfÌVjró¼ÊŒÌ•ÙË|¼ðL’vïlÇÉÀ´*C‚üô_ÊÇm PýX ¨ñÈ=À;ֈ缹e!M,à-²7‡R3±Áíu²œó3Îe²¸‡žy@ó((̈Æg„Py {»–ʤj®¡PÐׂ%öà˜Ì:f,øÒ­&CcÑ®d¥”Ò†#/¶…&„BÂ\ØH©½Ö–²›N,a$ß¾Mnô!Ìà:§ ØŒ ;$7„Ãj¬‘0çËì~€è®ÚòÔjxïédj†l#/~¿¯T~?"úµ¬%š—ò>œä=ÆòLo2±5!£À*€”‚}î·‰“¥D*Òs¬ðf…Ò³½1±fs“W‡D<=Áî^ÒØÈ÷¥£HQU:@ÐZX°æCV‹FѱD»¬ëw*I0 65'mû_±okV-ç–ºÕC0x5EÐØÑ¡Æ”£tZJ²F08ä àD¦@«fÞ­QA¬kšâsmͨõvíœiêGy£YUSq"T'†+·AêB¸`daL‹šbBÌQVd²ÕŒ{†±&0dUC¥›ÌwÓ6Î=>rFža†ßFv¶´O9×"}ª(ÙZ]›`Ú@ÚUÆ#¡DÐj¥†#2Yj2ZÝL3^‡võEÞ““')Œc !b3ä+•„nãËÃxºÍKÌÊ#c7ƒ´lÜA£A¶lȱÖ6¹¬5©Ñ™¼I¹s{)FÛmbm¥jZ#/µH›j¼0‹$ÍË_¦÷u®Œ½ÉÄ*qÔŠwœ#5Fu(3»ãƈŒ)a)ÓpM,(PÊ ©ÓcåƒfHÃSKÅ2¤Ô1wÌ¢ˆxpk3Žâ †iYŽ.Ílo! —R¤Æ±¶–ˆ@Óê+\ФIÛR¶Â7vÕÛmá¦oĨ#/)Âl¤‡æÂ41‰ïlñBMÐÑ¡ƒ[:JÓ6Ôj©je*Š¢…Xa†ñ“LV1·š‹.„ËÛ—KG]ÈŒ3…Ek©Š´š˜:¶†›¤†@aú°wmݳ hq²iƒ2òÈ ¶ûiæqõ<âñ˵aQÓèÃÙ”~n¡£UJ5™b´à£„Bc9GpáF2›¦ Q½åƒUƒ#cDÇF2#/BØR…hl;™1F%Ž´c4-]Fi¶pÂyë+`æ­z¥W¦dpìT¤ŠçaÌá“#jP˲ÆÔ£„rªÛ°ê ƒÐÖ?„… ­GÒ¦fn*£âjˆ‘ˆR-yZ«Œzp§m¥W—”\8@s¹eÇ-:1ês[u¡ñ­½,-™J`qšu6¾i¶Œ.òaZ³ JF™áÜ$go$®Öi‘Ôàè-2“FHÄFK3º*ÁÁ…ÓŒ»!^ž5¤E¤ŸIPÁQ®úµo´#/:Žp0ãG 0%2ÈÛ¾ÂlDqJs;¤*õÇŽ§åó„}+?_èX~×;ˆÒh©4n>õæóξw·¶zê_Ð×ÒŒ3+Ѧ#,B6i±J>°X"B“´*Q)#5UHkø¥2Iâû=µ(Òe!ߧeS¾¬gy­[€i T¦2Û³÷œŠ $UUFJ2ßê*{ÑŸñ/PèEÏó+ÚIŸCªšc‰jó=¾¸CÉûÀþ^°æ‹ëA"qm!–·JÍÈÍPƒ€ñÂ@A²ûµï¿JÙ&a28÷bÍ„Ú[Ýøš§Õ„zÞlº… uq–ÐúðöîF›ºNîäÈ!ݽx÷õ5¼i]tjaQp%-T¨Á³È3=‹g›ÝÆuë2‡^Ð÷¢? ;Q<ÍùT¡“ŒHQˆ á6o-ÕA#ôÏË9ž½Â(Vk{–±´à‘À]‰LÎÈ€ôRƒQ‘a#,X°Aˆ¯)¶¹µ«¦ñ­kË«¶¹¥R«ÍpÝüSòEæE?Aß×ß(Ò}ü$$ŒcPÙQ²’¶Ñ­(ÔF$ØQRRa¶¦VË2j’Ù¦ÅZÆÍ)*‰-&6fdÉ¥–Å)HÊZiR”fJˆ¡Šm¢l”li²Rš,Ô©¥h5"É› 0Œš2[V1­­üëßÒ¿%Þ‰‚ ñÀö¼«hâ/¶%¸PyòŒå¸ûæåteÌÄóÜýOÐ×ó°›ü\÷Õêî¶Ï\ÚD‡ÆsVÑ|’aAdƒpp½ïj–«U¬ZÊ#/5ÂþGŸ§Ç4AóÙÔo³Dó.F#5ä´v‚~Y»zOg†ƒƒM#55n’öÄ 5/öî„ð )"‚‹¬kI­}æÕ®[1¬)4H)-A›_r]¡j•BÅ#5PÅFR>´²eÏ€`'qxå!BŒ*O·Û˜\3W@Ëd}ö^¿°¸j@j" þ˜QjxoCŸŽ]+L"k¦VºŠŒ èËRûŽQÌE4\#/4‹¡w›¢eÙ“$ÂXÃ#,4Ž±w$Âl&‡…1H ªÏmM–ê”$ÅP* ÖXj%í%†¥ñÝ›&õÙcßÍÕõÍz“zÑmF:š¢¬´ò½¥*KHÆ‘#55Q’¥Ñ#5E#,ï8ã'áí5§æá ÙòÅ6FK^΃ҺâP{7¿g_©y»Þ-FÞ­»"’bTi²ƒYDqbÊÿÙÒÈlÐ.g›.j˜R´4•/ ¦d£$1A€`½Qˆ<,1‚:5$m|Ú¬ 8tˆDn`*LCq-ä "I$‘ÁPÙ·‡dœÐʨg˜b¥(¡˜HѨ€7$Ç¡æi;¼ÄMó­#/é\W‡_78ÏÚ•×(,ú/dî"B_òá6ø0 hú½*s¬añ²‰ª£-n]MµA}êÂRЗ0 úT%Ý|È#,ñ‚¿ ¡p˜{ ÄW(4ôçèþ·Œªôn%Ô¢•[«xc%Ö~~>O–ºèjIÛ:ãØ?‘ëSQž‹ñ*ûó)¤ÒÂ:#Êse·ö˜#Ø8…ïúèbáöôaç¦~ÉäX2ŸaÌÈWJñÆÓ­Œz™TZ™ÎÚäÉ64.Œ#51B}©%÷s]kÉ^vœƒ-¾ßaî°i«Ð=v²¾´äœ­r4ié>4"’§< f׸‘©"ø5,:ã»;í˶LÏgÕ&åØ›>f;}J`8§²$DW‘ë5êOñšØ§¦#/‡÷œËEŽç`VŽ@)2g³š Ú611ÿ}XwÄh7!€ì#QýÿTÄ7vuI¨™3>Þ_b7Olbwr>^þ‘BIDI(€Š%çìBöÕ(´jtû´íƒ }'‘n,,)Ÿ¿.¾¯ÏD~ìèGëì~Ç­n~MLáØÎôFB«¦‹ PÆ,+rc¹`Ã6'^ž]hlÅ0ÔU÷¡ƒÑËqQ$îö¡š©*}_m[îÃx`¾fo\e#/1IqˆoÁÚkö‰¼¤þB%„Ž¹°ÙW*Y5ÔX6Gîg|¢ƒ]82¿’Ü1´„–‘™Ÿèw»Þ:*DôyG7p‘l¹ 9R%T:­+ŠŽ\~q5Œæg”à§FÔ²4Ã0ÖÚbj„‚"Bl3Æ 2&#,¤LífÚŸÔZV’ÐÈúˆ¨ª) Ìú—l!Ã4ÈvWº«²™º"¨ih±bE `BFÈ„TIx6;8ØÓâ„Gx£½¡Èk™¡¤’9É¢K7PeÌÓ=™Ø#,˳RƘÈ­h¦RĹ”6ºsÐÙLv‘ƒª³”¬A‡8¦Më¥×'“&èy¶ ÃD…ˆ³Z(`€nQ©ê?i˜` `‚PÈÄ̃Þ"¸7§VŽ¨hͤ•B[ÊBÒ£SÈ4,U±M‚FÌÝîãø¥‘Y-…mÃs=rkš†™Ü±Qš ²dãg[YšÔE`؈‚Ù™ƒNcQ¨œWõúù¾¹Þ ûëgC‹èý #àSAcÕ›f&3ÖF|N.š°cðœ(F®u0ù„õ\ô€Ç;9ç5`Ž¯…í.†êiа;eÛCÀûï&:V3+»ä:ô)˜–t[½C‚;Î’Š[„.êö†5¦9Î'½‡<³‚XÐŽ8-TÑGˆ’Iº›AÊ… °4IAŠ’³#5Ð(„4Ãà˜o³Â^+Þž£ÈFÝøf´kq’Ç—á“SîÃJj—9½Ú›Ê㶠cïLé”hG“×2©á¤AkHÌŽqlcŒ°—Ó`CF”„8íîŠã ÎöêØDI"ÒÆL¨¬2‹™C4 xv´ÚMBÔrç4è…"•j‰D°Î²Òb|ErEbÝc,n#/3AÃkPÁ- A èw ƒ¦¶FH¨åJKy|2”˜LÕÙSÙÚâs\Iu`B2 ëÆÛ#/H³%@òÚ™Þju ©æ>Ír+R–fCÂÐe¶2g=×Ü×mtyBçC‘±Öi ¡f°E2Ç#,Ò7 ƒ@毧̇jXH8ºÎ.Q0tWF;bÑtç±Ócir8ŽMÒ(e#?ê‡h0._Òƒ Ì–‰Ó57“HaÊ< $t3 Sl†tÓi#,s´“Uˆ²P¢& mð±N5:þ[VÆ-ÎPd+‚¶’к0psW)„‡.˜(êã–Kiä¶ 5˜šf…PŒ`dYˆc0’“™Øïú×;ËÓð1 ¸ë\‡Zg;‰Ø0SC#,šÊ©&;XÒ){(zžpÛhK“bK¶èä&º†2Žƒö_ø6˜¶ÎS9’Ô`ôÊŒ¯ƒòö“wLôöx€51X0“Í“¹š£~G36‹ xÑ4õë~F´(ã›ky%\»}ˆGGѼ³Ñ_ k§Žp9kI©X«Ob\8W˜¬L°ˆ3‡f²á G”› ¥£íéxıu»Ú“¶¸ò• #5ÒtÚš–b¿o[£¦ À"l]D¥!Á˜¡*£ó~í¥¥÷ ƹȴš·¿¼7½NP¡Ù¨‚Ê]<ä¹nî7ƒ»åZ íû‚ï¨ò žzbŠd׎(ÍÙ1Ä0ã2ölUñ7’¼–¹¤å£^·*éÁX)ðžäŽtÂfªH›¦ -¬¸1laù¦‡@Íå1¯GYÛv"öÉ,˜;ç„ÎCÉœÉÕZÒ¾Rê41ë~¦XŸŸ•ôÎ0ÑÈ*ÐÆ»ùœ‚#FÎf;&Úlðæd#/ø´3J±v2›§œ¶/5Ê·æF*VøDtCH>4š[ï ’u,w1Œ`¢ÑÌËá¥GBmðX¿cõy­ÓŒÞ0fßE4Q«HˆPÈz!‹ªhEçAFH"¸ªPÝÖMO6÷ÜÉÂÄ;½S¤]Áõë- 2m)\g†`uns…˜®EÌv#DÌ" 3µœúQ4ºÙ"6#/KÀ2ÁÐÃDb¬HªŒbj0¦Iz©#/ÎUñO/wÉHRöYu,««DëJb(‘ÙÅ®ÿ#,Ì1aª"/eHÚ‚žhïCÀø3bi˜RœÊ»ƒÀ CiÁ¨Q Á’pìŒÆá Äaƒ¨bb_BÖŠ*EsI`dl!‚LZÐ6؈‰ ìªXò¨¦&Ý„—²z½}'Ñu”¹F TTEA³GnP]f^®¹=£`Ë*„;E„HH)¨ 5#,æE~íÿÜ[ײöUAÖs—¯}Ø* &ɸ‰U$_°. Äh”"%Q Èvøž@PÈO.Mæ31<Â+"Æ’©bP2Á8âd‚TLc`Ç4kÄÕçxKÍÝbT®[ÞëA±ä! d… È𢢌A¨5%TABJ‹J :éˆã#, >l|si(A¤:ënKßwL¹C®’dˆdÐØ&`¬R, „Nˆ{úÓ³´|û¼B|&>~šíFù ½pà‡ˆ)òáõ´Æ~嚈•Î†Ÿ×3¹FŒPp ‰R‘Ks1ŒµVDD Ð9†Y~И]ÄcaS&¾¨™Jëä%„õuØ'CŸAMìM€ð iÉîŽã¡L’xØÃ)âA!ÜH‹Žû"ÌCÉ©ä±Í#,…0¿‡B°ˆX¥ àïÙϲ؂l‚H„€†a>’6LNpNþ=AÓU7ö#5‡h‚˜|>#, t"qvõl"HÐjá†Tvè®#5dÜ:cD“A‘„\r,É#/ör}°áM‹BÏÌ~eŠ0„a#¥¹#ÏK5 Nè"‚i¢1XY«:ÔEŠñd<¹饤CM"ò£¿!Ý,FÙ‡†a­4¦…ªÂšØ}C¹Ï;PÚC-¶`¯³k9NPïÃ+Zë-sŸ‘‘ƒNÉ ; Ô÷gÛl¨í­õÛ”I°6“jÀ*á,YpèhYhý[¾¸©¼ïðQãU#,E€§¹% 6Ø¢j2‹•1pA-šý–a]Z³š‹06ˆpM Nÿ¬NJJ†RS,Ûs#,Þ&àŠN|P¤¡#,ë:Ûv¬®¾`R)eZ·Ò —Aý‚«! Ó.¾™Âþd|yõl C7Y0Ó®e‰ÖpLCÍ8äP$ E`AAÀU€®ç²=|°Mß#ÆƉòõª×aÈáÐ-@ª!RÈ.B¨6#,R¼„j#5~'»èý½ø$Àce„Yˆž>ߣáQ¼æÅœ+4¢SøÑÈtx×_¢ŠRÉk ‚ß…Ãð?ɨ|>>ïo*ÌŒ>k,"˜ö{M þ3Ah„'ÿÕ·Ä–ŽoN憨m·4kÊ«@(í#,¥$HßÝE‡*oßû©hÚä¾’¿ª ÝZDúbl *|ûÇd,)À_ï€vX.Øbvòر-¸$HÈLÈÅç\¬VaI)"îåŠûžtÞh5xñ^5û’çž^o+Ï|ñM¦«´E°ÙT"6¯uÛ¨à &³KQD™ Q底6Ó6†˜@J” *pþ$Šl¢›OùÊ_w›¼½y$†@!» ¼9›Æ@eÖD«M"¦!ë5ùßHö#,õö‡ok"È,€ÁH1FItIÜ‚Œ‚HKJ“$–*eh²YfKf’m_õãc}ÍWòõô)TC™Qª4še,Òîü°IüOÈEP¨‰Ÿ²–»|Æf7Ò@,)ºî¦I#,àOØc_T½´Ý×6bldÔM¬ÚŠ*Åe¦FÒ{çžjˆ@B {(#,´D"ïŸóD-ÍA5ÿ‡‰˜uw“âú¾¸Á×Iâ¯A5ÕÁ¾Cxtœþ{ŠÊÄ "ñ½WDLp1 ÷¢ž$‹Á¥ÇŠ3˜)–S T‘²Â½´`ÏžfÆð)B°ÆÄ%—óéA¸{ù›iq¢à2 Fû˜ã†1Øž¥MÂÀËCÚXS¢yL@A|Á€A¤Ím6´”„$µÍÉ­R­)6¦Útï–¦‚ Š)sVÛ7“Tì"®J€Ø=Z”`J¦H(HöÚÃe·XOn’ãK¸Ý¨ÕZ¦µÂ} @xÉàRŽ˜ô<ýT-ÙÉiHØœý4#âì:ÆØ£"H›hx’öBVÜÛl Lµ‰Ž„À?,·ïgÍóºÒå†.[6oYhD x˜`næçü¥¿©¶ÕÕúº¾¹}¾:(52&‰RE‘¬SEŠ)(*"Ú2UB•È­úm¯5#5- ÚJ€¦§á•³ñQ –|dK,'Ü2n„ŒD*–QE1cG¢Šh™ññ5± *…¡·`'¥ïŸ\p]»% n»š ´È‚…dCDZ‚´¡¬P›i²ƒ!ƒ$`'‰µòíÌÛæÎíí¥VXjc·Õ¾>øÁW6¾¿9Ž>¢ZØ–0l³(CÝÚmwû š_©ý~C´+CaXe²œÚã-‚#"T²44x d`;ý´:`&ÍlkrÜ’•}}«•¼»¨’Ú*+|®ëÎ˱\ÚZõ©ç[[Æ£E¥™ªÏrGë6³#¸Qý¥d8$-#,Ì,&íOQÏ¢I¨u#œˆr"ĺ3Çü3AZM±¦¬M ( pÈõhX}tfò]JCñ`aÀP®%Li¾pCq5‚u‚]¶ÅaÕ%A‘B0Xs¢k”´nåÕã^o*ëIbÝš™™UÜí´Ò¦Ô¡¯{µÎV+*TÔQ±­çvךû+½¡A#,¦Ac)H&5„h`ÆÈÚ[R•¥4ËfZé·nè”Ò ‚ˆNE¡RDBNεm!‰°éžXK„=¢ºÿÛ—òà«‚Yƒg±¯¤Uòf┘4Ñ8/Ž1†aU3ç qyjI²ë²©Ùíîù"Ö/PXL*&†©J8»÷zÞìñ0lèÑcMû=#/fü…º³ìÛÕB(£Š‡"­yšëÚ%ôÉ!ô^ˆKž‰ BA¢"˜5‰#/ðè:8$ÉQ;’ý 6’_éc MæÿÀZ׎§_¬•¥Çk“Û|å†ýi‹in‘Û¦Jµ ª-ä~?Prg´š¼<ûh_cBQ‘ƒQºô0m­Têð#/(PD>›r8”š_™­ê`×L‹ãõ9ô`¢ÀŸ~úÙ¤q<–ó€’Ešôbä¡HXÜáÛ(Mˉ‚„KÆV#5vÂ9¡4k%Pm¬ýLzŒlŒ¶Œ"À×F\¢é¿HbqK÷M|§hͤ†ÉpdLT“‘=Ic0äd¸(ŒR„¥ €CJâ'#Ãg¥1üÙå—÷#ö^3À!Ç<ª„¤2îz?2P?ø>³Ê¿DÉB6 ʇr³¯y±cÞÀÉï;ݦ"ó¥jIb8h™#ߎ'\¥„Ç:-¥i#,¾e#CûaU°þ¦ŠùŠ¶Ý¿hzž­:µ9´ â(dÿj‡©†È˜2*&½5趙®õmÑ߶…ÆÆF Ú[âôv3óŒõÞäB4Db†'RÖèmI€*™‚ØB#˜BC¡â”_óÄÈé†`– ¯N—À±¤ˆçç"RÚº#,“m2hæaŒ‡/b"fg;Q‰¬ãÛ¬˜‚ö ¬;h-Oî5z¼ìw™hiìºâˆ¤ýØA´ ¨cñ¯Ž+qÎz)\ X¡7 uâÆtù–‚ åþ%)`aá<‘êz¢¾*~LÌÁù]ü7¼žÜ4Ò÷̺‹DSo·\-baúâ.èg_îþ_áò~øÿ»ýÿîÿ»þͶ¿þ^Ïû7¿ÿ‡þ¼?ãþ_÷}ÿþ¹~ÿü<¿ŸüÿúßÿWûçÿ<ßønëÿ—§ðéŸþ?ùYýÿ÷ÿûÿ‡§þ{>ð³þ_ñïÿ‡øËÿχåÍÿøÿËþîÿ¯Ü~O÷þÿ¿7ßùéñ87ÝõõvŸù]†‡ô‡õ«b"jy6Å5M…\?Ð9ßJ<˜«î°B U!œpEÝ!#ZûW‘nóøwïi|§ñyÎî™$Š¢1Œy„ô2IRC²Šl`†éÝG¹¯(Ay4Ú!®z©¨§#,˜6Ç3ø]þꥂƒÜ?îßžÔÓi˜\Ùp–êx†ëqFï„_÷TK´,ã5(„æ)A@µ|nÌ&8ãt§00ÀQa×úèS<šßSÌÿxÿ/ûÞÒÊ?÷yšz6¦ÌÖÐèQþmIŒ‰ôŠ‚Å ºI'ˆÈeŒÌ ¯I?ôÍ@œIÀÜq¿ ÿ•ieqÖ&!3"…2*×r­göòì {­0ªB Ü”Ï&QFišÈ4).¨±ƒ—Y/$³ýùã’ìç1UjUø¦‘2ðÍD)(»p®ýq¡X0š+šANL1#/(=í8C(hškT˜mѤÝEpq1›ˆŒZæá–°ÛÖÓF˜›H†CÄ#/ÿeÉV×#/(k†â¯ÙæIJƒˆ”iåÅÓ%KK¦#/¡²°(R°™ÍŽÆo®'?ì&#/ôçÌö~¨]1ì¾3.ÊbVæ.Èœ¼Óò™àƲÌÌ5¡‘Þø(ÛÚ¿¤—^Íár#%µíÒsVoîÀUÓØÃy…I9óovÿy–#Ûî h1‡¢ #eëÛ³¶5c+KMoÎÃX`­±Œ9¤ž$½CÛ#/V¡¶çB¡<)aó¯´×»á¸ F¼6 ».ˆ¥ÔÏ×·*•ØŘ/S8òÒ`y±3DEa éŠHÎûs#1ïÎËXÍ¡&Hc|·E!Š†Q‹{ňPÑ~––2© E$€¨šy‘¨§J'\3Z€#,À’1-î ü ‰bfi¾ØX(/Õa狸1ågi±ZÒ#‘»#Rý^¼<À¦S ‚ˆ‹ ËJ%B…ATZ²”ʶJÒkL”²C`ÆÊú]ªå#,2Ê€qS#,™?Þ@2ˆÈÁ?ß&<*›(«yÁ-‹'3Ù( %ÂT@¥b(yóúCèïGU¤?ê‘Œ‰ yNð`ò54±Í%2gúF„c!t§“ì#5„Âdx‡‰útttþó‰,¤TG3‡ êú¥#5%±œsøâÓoæ0Ï{BŸûdk»:uNô§VÇë*K~¯xÆ=%ȼ©¾=Í©ò°ööpcõ•]*Se`nL2\aÓs.® #^áÆÙ²Ñ>Ê,7Ÿt›Qyä>–*m4ØšÙîá=(Ýá{.eoÆD'4Š°‡TCVzI”D%%Ý€-W­nm¥•´¥ëk^·ÂÕ}Ëã­IQTZ“D³hŒÆ#/1(мf±Ó¬"5Ýa+O(~^“ÒõÃñ¢ K‡?ìÄø KÔiµ @r19Nac#56œìÁäK–*%*¸”øóèI}0í$&PšÕk##=”´î#5'¯òŸe»".hp ÒH²!!7’æE Ðö‡qFO$v£“íà'‡ŒÿØ ¸zäj*„bECkµÇf•„”ÕË›­«%jF¸jûr¼Ûo%Wؽh›53_•ZüÈ£h€#,ˆäw·Ïá™åóÿ×Ö‚w!=~¨©ãå(ø{f8ò0m¶ÖŸŒx;.‰x§ª#/gȤòû?ñÿ¡EB§ýZþvÌÌ#/l‚Å'ù< Ué6áæçûÔU'ý]¿Å¨£Sõ#/ J®ýá—Ò>ý{pæWi.ò#/¹ÿÜZXßæ§wú¿ì†¯SüþÓúùµµ™ù#¥l³·¢1¡¨zeë¢×Ç¥vñ‰’©œ~®yËAƒSù>¶ÉÓ¶ˆÄÆVJsøzK„pYkϼ#Dâ¢ZÍÆ! ^G¿§,ÀŠÃ$ Œ}ûYOÎÏužº6³8•È'ÇõçÙL*Ï«Ï£o3¸žÍ~ŸWüÕ‘àRsŒâ;Ó¹j¬b/È5Y_ç²V7Ä|¤É{~¯ÿþ.äŠp¡ Ú‹ê– +#<== +#-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2\n\niQIcBAABCgAGBQJWpU3oAAoJEGelZe39+Q5kv04P/iyvALGAg2Z8oICEDjFkEXWW\nh2CMGLItAhqb3xNeV8WUMMpY4MbRRpN6cU/SPmt+as4oVn2pozca93eWD7yOxukK\n10seOyLTBamS0Wf+BNr6jYXZRQ2N7inc2p6AD75pMOFSg2HeIeQJ0aUIAxNeeojZ\nmUiLYMdtcrF1Kh7KWZAkYSbIAEjJeobLqk2oY7UyqKcODc4RtZJV1InnO4DItEWD\nnd3F5kkVMw4pwYAXaikmCXYBKHXdF5w82KxqEjrAWSoULipX0BVCsSbQ2L0UOs5h\nKXUS4M7AaYKyCcO17E7CnVXaW+vOVyGEECxtSaExWgK5MvYHIGE1OFvb12PkUvUY\nc7CiBxk6X5eZkPyxgxDj20r8zNQVGZ8jDI8Wg08yTAl8+09qCtkE8gGMdNeHYwX8\n2xDH+A3+19022ZZdyO2t5+2AzU6Kkl1qTPKaXJWFRtr8ApD45Y4D3/GAsTNqdOMi\nWeh1XvqQdHjm9rEoJX8aBXShzCMCNhmZalbUhrdzQY6/hnl0PqnlPtyvtkjCvWoF\nXLF6q8YV/ZtqCc36vePZ6lpUQB6FG3g6fhMGraT2VOmT3TROcG17pqIz5y9+85xy\nVSaDc82uHlyzIsZ7vuhV6d9x4yXnFkjMAogCJv6mitFbQsd+LtXYkU+2Zq6wOoEp\ndLLfK0Km4Vs9FYAUbuUi\n=7D7V\n-----END PGP SIGNATURE-----\n diff --git a/gomspace/libgscsp/lib/libcsp/wscript b/gomspace/libgscsp/lib/libcsp/wscript new file mode 100644 index 00000000..7b9cbdba --- /dev/null +++ b/gomspace/libgscsp/lib/libcsp/wscript @@ -0,0 +1,346 @@ +#!/usr/bin/env python +# encoding: utf-8 + +# Cubesat Space Protocol - A small network-layer protocol designed for Cubesats +# Copyright (C) 2012 GomSpace ApS (http://www.gomspace.com) +# Copyright (C) 2012 AAUSAT3 Project (http://aausat3.space.aau.dk) +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import os + +APPNAME = 'libcsp' +VERSION = '1.5' + +top = '.' +out = 'build' + +def options(ctx): + # Load GCC options + ctx.load('gcc') + + ctx.add_option('--toolchain', default=None, help='Set toolchain prefix') + + # Set libcsp options + gr = ctx.add_option_group('libcsp options') + gr.add_option('--includes', default='', help='Add additional include paths. Separate with comma') + gr.add_option('--install-csp', action='store_true', help='Installs CSP headers and lib') + + gr.add_option('--disable-output', action='store_true', help='Disable CSP output') + gr.add_option('--disable-stlib', action='store_true', help='Build objects only') + gr.add_option('--enable-rdp', action='store_true', help='Enable RDP support') + gr.add_option('--enable-qos', action='store_true', help='Enable Quality of Service support') + gr.add_option('--enable-promisc', action='store_true', help='Enable promiscuous mode support') + gr.add_option('--enable-crc32', action='store_true', help='Enable CRC32 support') + gr.add_option('--enable-hmac', action='store_true', help='Enable HMAC-SHA1 support') + gr.add_option('--enable-xtea', action='store_true', help='Enable XTEA support') + gr.add_option('--enable-bindings', action='store_true', help='Enable Python bindings') + gr.add_option('--enable-python3-bindings', action='store_true', help='Enable Python3 bindings') + gr.add_option('--enable-examples', action='store_true', help='Enable examples') + gr.add_option('--enable-dedup', action='store_true', help='Enable packet deduplicator') + + # Interfaces + gr.add_option('--enable-if-i2c', action='store_true', help='Enable I2C interface') + gr.add_option('--enable-if-kiss', action='store_true', help='Enable KISS/RS.232 interface') + gr.add_option('--enable-if-can', action='store_true', help='Enable CAN interface') + gr.add_option('--enable-if-zmqhub', action='store_true', help='Enable ZMQHUB interface') + + # Drivers + gr.add_option('--enable-can-socketcan', action='store_true', help='Enable Linux socketcan driver') + gr.add_option('--with-driver-usart', default=None, metavar='DRIVER', help='Build USART driver. [windows, linux, None]') + + # OS + gr.add_option('--with-os', metavar='OS', default='posix', help='Set operating system. Must be either \'posix\', \'macosx\', \'windows\' or \'freertos\'') + gr.add_option('--enable-init-shutdown', action='store_true', help='Use init system commands for shutdown/reboot') + + # Options + gr.add_option('--with-rdp-max-window', metavar='SIZE', type=int, default=20, help='Set maximum window size for RDP') + gr.add_option('--with-max-bind-port', metavar='PORT', type=int, default=31, help='Set maximum bindable port') + gr.add_option('--with-max-connections', metavar='COUNT', type=int, default=10, help='Set maximum number of concurrent connections') + gr.add_option('--with-conn-queue-length', metavar='SIZE', type=int, default=100, help='Set maximum number of packets in queue for a connection') + gr.add_option('--with-router-queue-length', metavar='SIZE', type=int, default=10, help='Set maximum number of packets to be queued at the input of the router') + gr.add_option('--with-padding', metavar='BYTES', type=int, default=8, help='Set padding bytes before packet length field') + gr.add_option('--with-loglevel', metavar='LEVEL', default='debug', help='Set minimum compile time log level. Must be one of \'error\', \'warn\', \'info\' or \'debug\'') + gr.add_option('--with-rtable', metavar='TABLE', default='static', help='Set routing table type') + gr.add_option('--with-connection-so', metavar='CSP_SO', type=int, default='0x0000', help='Set outgoing connection socket options, see csp.h for valid values') + gr.add_option('--with-bufalign', metavar='BYTES', type=int, help='Set buffer alignment') + +def configure(ctx): + # Validate OS + if not ctx.options.with_os in ('posix', 'windows', 'freertos', 'macosx'): + ctx.fatal('--with-os must be either \'posix\', \'windows\', \'macosx\' or \'freertos\'') + + # Validate USART drivers + if not ctx.options.with_driver_usart in (None, 'windows', 'linux'): + ctx.fatal('--with-driver-usart must be either \'windows\' or \'linux\'') + + if not ctx.options.with_loglevel in ('error', 'warn', 'info', 'debug'): + ctx.fatal('--with-loglevel must be either \'error\', \'warn\', \'info\' or \'debug\'') + + # Setup and validate toolchain + if (len(ctx.stack_path) <= 1) and ctx.options.toolchain: + ctx.env.CC = ctx.options.toolchain + 'gcc' + ctx.env.AR = ctx.options.toolchain + 'ar' + + ctx.load('gcc') + + # Set git revision define + git_rev = os.popen('git describe --always 2> /dev/null || echo unknown').read().strip() + + # Setup DEFINES + ctx.define('GIT_REV', git_rev) + + # Set build output format + ctx.env.FEATURES = ['c'] + if not ctx.options.disable_stlib: + ctx.env.FEATURES += ['cstlib'] + + # Setup CFLAGS + if (len(ctx.stack_path) <= 1) and (len(ctx.env.CFLAGS) == 0): + ctx.env.prepend_value('CFLAGS', ["-std=gnu99", "-g", "-Os", "-Wall", "-Wextra", "-Wshadow", "-Wcast-align", "-Wwrite-strings", "-Wno-unused-parameter"]) + + # Setup extra includes + ctx.env.append_unique('INCLUDES_CSP', ['include'] + ctx.options.includes.split(',')) + + # Add default files + ctx.env.append_unique('FILES_CSP', ['src/*.c','src/interfaces/csp_if_lo.c','src/transport/csp_udp.c','src/arch/{0}/**/*.c'.format(ctx.options.with_os)]) + + # Store OS as env variable + ctx.env.append_unique('OS', ctx.options.with_os) + + # Libs + if 'posix' in ctx.env.OS: + ctx.env.append_unique('LIBS', ['rt', 'pthread', 'util']) + elif 'macosx' in ctx.env.OS: + ctx.env.append_unique('LIBS', ['pthread']) + + # Check for recursion + if ctx.path == ctx.srcnode: + ctx.options.install_csp = True + + # Windows build flags + if ctx.options.with_os == 'windows': + ctx.env.append_unique('CFLAGS', ['-D_WIN32_WINNT=0x0600']) + + ctx.define_cond('CSP_FREERTOS', ctx.options.with_os == 'freertos') + ctx.define_cond('CSP_POSIX', ctx.options.with_os == 'posix') + ctx.define_cond('CSP_WINDOWS', ctx.options.with_os == 'windows') + ctx.define_cond('CSP_MACOSX', ctx.options.with_os == 'macosx') + + # Add CAN driver + if ctx.options.enable_can_socketcan: + ctx.env.append_unique('FILES_CSP', 'src/drivers/can/can_socketcan.c') + + # Add USART driver + if ctx.options.with_driver_usart != None: + ctx.env.append_unique('FILES_CSP', 'src/drivers/usart/usart_{0}.c'.format(ctx.options.with_driver_usart)) + + # Interfaces + if ctx.options.enable_if_can: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_can.c') + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_can_pbuf.c') + if ctx.options.enable_if_i2c: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_i2c.c') + if ctx.options.enable_if_kiss: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_kiss.c') + if ctx.options.enable_if_zmqhub: + ctx.env.append_unique('FILES_CSP', 'src/interfaces/csp_if_zmqhub.c') + ctx.check_cfg(package='libzmq', args='--cflags --libs') + ctx.env.append_unique('LIBS', ctx.env.LIB_LIBZMQ) + + # Store configuration options + ctx.env.ENABLE_BINDINGS = ctx.options.enable_bindings + ctx.env.ENABLE_EXAMPLES = ctx.options.enable_examples + + # Check for python development + if ctx.options.enable_bindings: + ctx.env.LIBCSP_PYTHON2 = ctx.check_cfg(package='python2', args='--cflags --libs', atleast_version='2.7', mandatory=False) + if ctx.options.enable_python3_bindings: + ctx.env.LIBCSP_PYTHON3 = ctx.check_cfg(package='python3', args='--cflags --libs', atleast_version='3.5', mandatory=False) + + # Create config file + if not ctx.options.disable_output: + ctx.env.append_unique('FILES_CSP', 'src/csp_debug.c') + else: + ctx.env.append_unique('EXCL_CSP', 'src/csp_debug.c') + + if ctx.options.enable_rdp: + ctx.env.append_unique('FILES_CSP', 'src/transport/csp_rdp.c') + + if ctx.options.enable_crc32: + ctx.env.append_unique('FILES_CSP', 'src/csp_crc32.c') + else: + ctx.env.append_unique('EXCL_CSP', 'src/csp_crc32.c') + + if not ctx.options.enable_dedup: + ctx.env.append_unique('EXCL_CSP', 'src/csp_dedup.c') + + if ctx.options.enable_hmac: + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_hmac.c') + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_sha1.c') + + if ctx.options.enable_xtea: + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_xtea.c') + ctx.env.append_unique('FILES_CSP', 'src/crypto/csp_sha1.c') + + ctx.env.append_unique('FILES_CSP', 'src/rtable/csp_rtable_' + ctx.options.with_rtable + '.c') + + ctx.define_cond('CSP_DEBUG', not ctx.options.disable_output) + ctx.define_cond('CSP_USE_RDP', ctx.options.enable_rdp) + ctx.define_cond('CSP_USE_CRC32', ctx.options.enable_crc32) + ctx.define_cond('CSP_USE_HMAC', ctx.options.enable_hmac) + ctx.define_cond('CSP_USE_XTEA', ctx.options.enable_xtea) + ctx.define_cond('CSP_USE_PROMISC', ctx.options.enable_promisc) + ctx.define_cond('CSP_USE_QOS', ctx.options.enable_qos) + ctx.define_cond('CSP_USE_DEDUP', ctx.options.enable_dedup) + ctx.define_cond('CSP_USE_INIT_SHUTDOWN', ctx.options.enable_init_shutdown) + ctx.define_cond('CSP_USE_CAN', ctx.options.enable_if_can) + ctx.define_cond('CSP_USE_I2C', ctx.options.enable_if_i2c) + ctx.define_cond('CSP_USE_KISS', ctx.options.enable_if_kiss) + ctx.define_cond('CSP_USE_ZMQHUB', ctx.options.enable_if_zmqhub) + ctx.define('CSP_CONN_MAX', ctx.options.with_max_connections) + ctx.define('CSP_CONN_QUEUE_LENGTH', ctx.options.with_conn_queue_length) + ctx.define('CSP_FIFO_INPUT', ctx.options.with_router_queue_length) + ctx.define('CSP_MAX_BIND_PORT', ctx.options.with_max_bind_port) + ctx.define('CSP_RDP_MAX_WINDOW', ctx.options.with_rdp_max_window) + ctx.define('CSP_PADDING_BYTES', ctx.options.with_padding) + ctx.define('CSP_CONNECTION_SO', ctx.options.with_connection_so) + + if ctx.options.with_bufalign != None: + ctx.define('CSP_BUFFER_ALIGN', ctx.options.with_bufalign) + + # Set logging level + ctx.define_cond('CSP_LOG_LEVEL_DEBUG', ctx.options.with_loglevel in ('debug')) + ctx.define_cond('CSP_LOG_LEVEL_INFO', ctx.options.with_loglevel in ('debug', 'info')) + ctx.define_cond('CSP_LOG_LEVEL_WARN', ctx.options.with_loglevel in ('debug', 'info', 'warn')) + ctx.define_cond('CSP_LOG_LEVEL_ERROR', ctx.options.with_loglevel in ('debug', 'info', 'warn', 'error')) + + # Check compiler endianness + endianness = ctx.check_endianness() + ctx.define_cond('CSP_LITTLE_ENDIAN', endianness == 'little') + ctx.define_cond('CSP_BIG_ENDIAN', endianness == 'big') + + # Check for stdbool.h + ctx.check_cc(header_name='stdbool.h', mandatory=False, define_name='CSP_HAVE_STDBOOL_H', type='cstlib') + + # Check for libsocketcan.h + if ctx.options.enable_if_can and ctx.options.enable_can_socketcan: + have_socketcan = ctx.check_cc(lib='socketcan', mandatory=False, define_name='CSP_HAVE_LIBSOCKETCAN') + if have_socketcan: + ctx.env.append_unique('LIBS', ['socketcan']) + + ctx.define('LIBCSP_VERSION', VERSION) + + ctx.write_config_header('include/csp/csp_autoconfig.h') + +def build(ctx): + + # Set install path for header files + install_path = False + if ctx.options.install_csp: + install_path = '${PREFIX}/lib' + ctx.install_files('${PREFIX}/include/csp', ctx.path.ant_glob('include/csp/*.h')) + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_lo.h') + + if 'src/interfaces/csp_if_can.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_can.h') + if 'src/interfaces/csp_if_i2c.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_i2c.h') + if 'src/interfaces/csp_if_kiss.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_kiss.h') + if 'src/interfaces/csp_if_zmqhub.c' in ctx.env.FILES_CSP: + ctx.install_files('${PREFIX}/include/csp/interfaces', 'include/csp/interfaces/csp_if_zmqhub.h') + if 'src/drivers/usart/usart_{0}.c'.format(ctx.options.with_driver_usart) in ctx.env.FILES_CSP: + ctx.install_as('${PREFIX}/include/csp/drivers/usart.h', 'include/csp/drivers/usart.h') + if 'src/drivers/can/can_socketcan.c' in ctx.env.FILES_CSP: + ctx.install_as('${PREFIX}/include/csp/drivers/can_socketcan.h', 'include/csp/drivers/can_socketcan.h') + + ctx.install_files('${PREFIX}/include/csp', 'include/csp/csp_autoconfig.h', cwd=ctx.bldnode) + + ctx(export_includes='include', name='csp_h') + + ctx(features=ctx.env.FEATURES, + source=ctx.path.ant_glob(ctx.env.FILES_CSP, excl=ctx.env.EXCL_CSP), + target = 'csp', + includes= ctx.env.INCLUDES_CSP, + export_includes = ctx.env.INCLUDES_CSP, + use = 'include freertos_h', + install_path = install_path, + ) + + # Build shared library for Python bindings + if ctx.env.ENABLE_BINDINGS: + ctx.shlib(source = ctx.path.ant_glob(ctx.env.FILES_CSP, excl=ctx.env.EXCL_CSP), + name = 'csp_shlib', + target = 'csp', + includes = ctx.env.INCLUDES_CSP, + export_includes = 'include', + use = ['include'], + lib = ctx.env.LIBS) + + # python3 bindings + if ctx.env.LIBCSP_PYTHON3: + ctx.shlib(source = ['src/bindings/python/pycsp.c'], + target = 'csp_py3', + includes = ctx.env.INCLUDES_CSP + ctx.env.INCLUDES_PYTHON3, + export_includes = 'include', + use = ['csp_shlib', 'include'], + lib = ctx.env.LIBS) + + # python2 bindings + if ctx.env.LIBCSP_PYTHON2: + ctx.shlib(source = ['src/bindings/python/pycsp.c'], + target = 'csp_py2', + includes = ctx.env.INCLUDES_CSP + ctx.env.INCLUDES_PYTHON2, + export_includes = 'include', + use = ['csp_shlib', 'include'], + lib = ctx.env.LIBS) + + if ctx.env.ENABLE_EXAMPLES: + ctx.program(source = ctx.path.ant_glob('examples/simple.c'), + target = 'simple', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if ctx.options.enable_if_kiss: + ctx.program(source = 'examples/kiss.c', + target = 'kiss', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if ctx.options.enable_if_zmqhub: + ctx.program(source = 'examples/zmqproxy.c', + target = 'zmqproxy', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if 'posix' in ctx.env.OS: + ctx.program(source = 'examples/csp_if_fifo.c', + target = 'fifo', + includes = ctx.env.INCLUDES_CSP, + lib = ctx.env.LIBS, + use = 'csp') + + if 'windows' in ctx.env.OS: + ctx.program(source = ctx.path.ant_glob('examples/csp_if_fifo_windows.c'), + target = 'csp_if_fifo', + includes = ctx.env.INCLUDES_CSP, + use = 'csp') + +def dist(ctx): + ctx.excl = 'build/* **/.* **/*.pyc **/*.o **/*~ *.tar.gz' diff --git a/gomspace/libgscsp/src/bindings/python/pygscsp.c b/gomspace/libgscsp/src/bindings/python/pygscsp.c new file mode 100644 index 00000000..c69d346b --- /dev/null +++ b/gomspace/libgscsp/src/bindings/python/pygscsp.c @@ -0,0 +1,61 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +#include + +#include + +#if PY_MAJOR_VERSION == 3 +#define IS_PY3 +#endif + +/* gs_error_t gs_csp_i2c_init(uint8_t device, uint8_t csp_addr); */ +static PyObject* pygscsp_csp_i2c_init(PyObject *self, PyObject *args) { + uint8_t device; + uint8_t csp_addr; + + if (!PyArg_ParseTuple(args, "BB", &device, &csp_addr)) { + Py_RETURN_NONE; + } + + return Py_BuildValue("i", gs_csp_i2c_init(device, csp_addr)); +} + + +static PyMethodDef methods[] = { + + {"i2c_init", pygscsp_csp_i2c_init, METH_VARARGS, ""}, + + /* sentinel */ + {NULL, NULL, 0, NULL} +}; + +#ifdef IS_PY3 +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libgscsp_py3", + NULL, + -1, + methods, + NULL, + NULL, + NULL, + NULL +}; +#endif + +#ifdef IS_PY3 +PyMODINIT_FUNC PyInit_libgscsp_py3(void) { +#else +PyMODINIT_FUNC initlibgscsp_py2(void) { +#endif + +#ifdef IS_PY3 + PyObject* m = PyModule_Create(&moduledef); +#else + Py_InitModule("libgscsp_py2", methods); +#endif + +#ifdef IS_PY3 + return m; +#endif + } + diff --git a/gomspace/libgscsp/src/clock.c b/gomspace/libgscsp/src/clock.c new file mode 100644 index 00000000..9e9a7d53 --- /dev/null +++ b/gomspace/libgscsp/src/clock.c @@ -0,0 +1,23 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ +/** + @file + + Required by libcsp. + Proto-typed in ./libcsp/include/csp/arch/csp_clock.h, but with different argumet! + + __attribute__((weak)) extern void clock_get_time(csp_timestamp_t * time); + __attribute__((weak)) extern void clock_set_time(csp_timestamp_t * time); +*/ + +#include +#include + +void clock_get_time(csp_timestamp_t * time) +{ + gs_clock_get_time((gs_timestamp_t*)time); +} + +void clock_set_time(csp_timestamp_t * time) +{ + gs_clock_set_time((gs_timestamp_t*)time); +} diff --git a/gomspace/libgscsp/src/commands.c b/gomspace/libgscsp/src/commands.c new file mode 100644 index 00000000..6abd3019 --- /dev/null +++ b/gomspace/libgscsp/src/commands.c @@ -0,0 +1,652 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static gs_error_t parse_node_timeout(gs_command_context_t *ctx, int node_index, uint8_t * node, int timeout_index, uint32_t * timeout) +{ + gs_error_t error = GS_OK; + if (node) { + *node = csp_get_address(); + + if (ctx->argc > node_index) { + error = gs_string_to_uint8(ctx->argv[node_index], node); + } + } + + if (timeout && (error == GS_OK)) { + *timeout = 1000; + + if (ctx->argc > timeout_index) { + error = gs_string_to_uint32(ctx->argv[timeout_index], timeout); + } + } + + return error; +} + +static int cmd_ping(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + uint32_t size = 1; + if ((ctx->argc > 3) && (gs_string_to_uint32(ctx->argv[3], &size) != GS_OK)) { + return GS_ERROR_ARG; + } + + uint32_t options = CSP_O_NONE; + if (ctx->argc > 4) { + const char * features = ctx->argv[4]; + if (strchr(features, 'r')) + options |= CSP_O_RDP; + if (strchr(features, 'x')) + options |= CSP_O_XTEA; + if (strchr(features, 'h')) + options |= CSP_O_HMAC; + if (strchr(features, 'c')) + options |= CSP_O_CRC32; + } + + printf("Ping node %u, timeout %" PRIu32 ", size %" PRIu32 ": options: 0x%" PRIx32 " ... ", node, timeout, size, options); + + const uint64_t start = gs_clock_get_nsec(); + const int time = csp_ping(node, timeout, size, options); + const uint64_t stop = gs_clock_get_nsec(); + const float elapsed = (((float)(stop - start)) / 1E6); + if (time < 0) { + printf("timeout after %.03f ms\r\n", elapsed); + return GS_ERROR_TIMEOUT; + } + + printf("reply in %.03f ms\r\n", elapsed); + + return GS_OK; +} + +static int cmd_ps(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_ps(node, timeout); + + return GS_OK; +} + +static int cmd_memfree(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_memfree(node, timeout); + + return GS_OK; +} + +static int cmd_reboot(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + uint8_t node; + int res = parse_node_timeout(ctx, 1, &node, 0, NULL); + if (res) { + return res; + } + + csp_reboot(node); + + return GS_OK; +} + +static int cmd_shutdown(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + uint8_t node; + int res = parse_node_timeout(ctx, 1, &node, 0, NULL); + if (res) { + return res; + } + + csp_shutdown(node); + + return GS_OK; +} + +static int cmd_buf_free(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_buf_free(node, timeout); + + return GS_OK; +} + +static int cmd_uptime(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (res) { + return res; + } + + csp_uptime(node, timeout); + + return GS_OK; +} + +#ifdef CSP_DEBUG + +static int cmd_csp_route_print_table(gs_command_context_t *ctx) +{ + csp_route_print_table(); + return GS_OK; +} + +static int cmd_csp_route_print_interfaces(gs_command_context_t *ctx) +{ + csp_route_print_interfaces(); + return GS_OK; +} + +static int cmd_csp_conn_print_table(gs_command_context_t *ctx) +{ + csp_conn_print_table(); + return GS_OK; +} + +#endif + +#if CSP_USE_RDP +static int cmd_csp_rdp_set_opt(gs_command_context_t *ctx) +{ + if (ctx->argc < 7) { + return GS_ERROR_ARG; + } + int res; + uint32_t window_size; + if ((res = gs_string_to_uint32(ctx->argv[1], &window_size))) { + return res; + } + uint32_t conn_timeout; + if ((res = gs_string_to_uint32(ctx->argv[2], &conn_timeout))) { + return res; + } + uint32_t packet_timeout; + if ((res = gs_string_to_uint32(ctx->argv[3], &packet_timeout))) { + return res; + } + uint32_t delayed_acks; + if ((res = gs_string_to_uint32(ctx->argv[4], &delayed_acks))) { + return res; + } + uint32_t ack_timeout; + if ((res = gs_string_to_uint32(ctx->argv[5], &ack_timeout))) { + return res; + } + uint32_t ack_delay_count; + if ((res = gs_string_to_uint32(ctx->argv[6], &ack_delay_count))) { + return res; + } + + printf("Setting arguments to: window size %" PRIu32 ", conn timeout %" PRIu32 ", packet timeout %" PRIu32 ", delayed acks %" PRIu32 ", ack timeout %" PRIu32 ", ack delay count %" PRIu32 "\r\n", + window_size, conn_timeout, packet_timeout, delayed_acks, ack_timeout, ack_delay_count); + + csp_rdp_set_opt(window_size, conn_timeout, packet_timeout, delayed_acks, ack_timeout, ack_delay_count); + + return GS_OK; +} +#endif + +static int cmd_cmp_ident(gs_command_context_t *ctx) +{ + uint8_t node; + uint32_t timeout; + int ret = parse_node_timeout(ctx, 1, &node, 2, &timeout); + if (ret) { + return ret; + } + + struct csp_cmp_message msg; + + ret = csp_cmp_ident(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret);; + } + + printf("Hostname: %s\r\n", msg.ident.hostname); + printf("Model: %s\r\n", msg.ident.model); + printf("Revision: %s\r\n", msg.ident.revision); + printf("Date: %s\r\n", msg.ident.date); + printf("Time: %s\r\n", msg.ident.time); + + return GS_OK; +} + +static int cmd_cmp_route_set(gs_command_context_t *ctx) +{ + if (ctx->argc != 6) + return GS_ERROR_ARG; + + uint8_t node = atoi(ctx->argv[1]); + uint32_t timeout = atoi(ctx->argv[2]); + printf("Sending route_set to node %"PRIu8" timeout %"PRIu32"\r\n", node, timeout); + + struct csp_cmp_message msg; + msg.route_set.dest_node = atoi(ctx->argv[3]); + msg.route_set.next_hop_mac = atoi(ctx->argv[4]); + strncpy(msg.route_set.interface, ctx->argv[5], 10); + printf("Dest_node: %u, next_hop_mac: %u, interface %s\r\n", msg.route_set.dest_node, msg.route_set.next_hop_mac, msg.route_set.interface); + + int ret = csp_cmp_route_set(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + return GS_OK; +} + +static int cmd_cmp_ifc(gs_command_context_t *ctx) { + + uint8_t node; + uint32_t timeout; + char * interface; + + if (ctx->argc > 4 || ctx->argc < 3) + return GS_ERROR_ARG; + + node = atoi(ctx->argv[1]); + interface = ctx->argv[2]; + + if (ctx->argc < 4) + timeout = 1000; + else + timeout = atoi(ctx->argv[3]); + + struct csp_cmp_message msg; + strncpy(msg.if_stats.interface, interface, CSP_CMP_ROUTE_IFACE_LEN); + + printf("Requesting interface stats for interface %s\r\n", interface); + + int ret = csp_cmp_if_stats(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + msg.if_stats.tx = csp_ntoh32(msg.if_stats.tx); + msg.if_stats.rx = csp_ntoh32(msg.if_stats.rx); + msg.if_stats.tx_error = csp_ntoh32(msg.if_stats.tx_error); + msg.if_stats.rx_error = csp_ntoh32(msg.if_stats.rx_error); + msg.if_stats.drop = csp_ntoh32(msg.if_stats.drop); + msg.if_stats.autherr = csp_ntoh32(msg.if_stats.autherr); + msg.if_stats.frame = csp_ntoh32(msg.if_stats.frame); + msg.if_stats.txbytes = csp_ntoh32(msg.if_stats.txbytes); + msg.if_stats.rxbytes = csp_ntoh32(msg.if_stats.rxbytes); + msg.if_stats.irq = csp_ntoh32(msg.if_stats.irq); + + printf("%-5s tx: %05"PRIu32" rx: %05"PRIu32" txe: %05"PRIu32" rxe: %05"PRIu32"\r\n" + " drop: %05"PRIu32" autherr: %05"PRIu32 " frame: %05"PRIu32"\r\n" + " txb: %"PRIu32" rxb: %"PRIu32"\r\n\r\n", + msg.if_stats.interface, msg.if_stats.tx, msg.if_stats.rx, msg.if_stats.tx_error, msg.if_stats.rx_error, msg.if_stats.drop, + msg.if_stats.autherr, msg.if_stats.frame, msg.if_stats.txbytes, msg.if_stats.rxbytes); + + return GS_OK; +} + +static int cmd_cmp_peek(gs_command_context_t *ctx) +{ + if ((ctx->argc != 4) && (ctx->argc != 5)) + return GS_ERROR_ARG; + + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 4, &timeout); + if (res) { + return res; + } + + uint32_t addr; + if (gs_string_to_uint32(ctx->argv[2], &addr)) { + return GS_ERROR_ARG; + } + + uint32_t len; + if (gs_string_to_uint32(ctx->argv[3], &len)) { + return GS_ERROR_ARG; + } + if (len > CSP_CMP_PEEK_MAX_LEN) { + return GS_ERROR_RANGE; + } + + printf("Dumping mem from node %u addr 0x%"PRIx32" len %"PRIx32" timeout %"PRIu32"\r\n", node, addr, len, timeout); + + struct csp_cmp_message msg; + msg.peek.addr = csp_hton32(addr); + msg.peek.len = len; + + int ret = csp_cmp_peek(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + gs_hexdump_addr(msg.peek.data, len, GS_TYPES_UINT2PTR(addr)); + + return GS_OK; +} + +static int cmd_cmp_poke(gs_command_context_t *ctx) +{ + if ((ctx->argc != 4) && (ctx->argc != 5)) + return GS_ERROR_ARG; + + uint8_t node; + uint32_t timeout; + int res = parse_node_timeout(ctx, 1, &node, 4, &timeout); + if (res) { + return res; + } + + uint32_t addr; + if (gs_string_to_uint32(ctx->argv[2], &addr)) { + return GS_ERROR_ARG; + } + + unsigned char data[CSP_CMP_POKE_MAX_LEN]; + uint32_t len = base16_decode(ctx->argv[3], data); + if (len > CSP_CMP_PEEK_MAX_LEN) { + printf("Max length is: %u\r\n", CSP_CMP_PEEK_MAX_LEN); + return GS_ERROR_RANGE; + } + + printf("Writing to mem at node %u addr 0x%"PRIx32" len %"PRIx32" timeout %"PRIu32"\r\n", node, addr, len, timeout); + gs_hexdump_addr(data, len, GS_TYPES_UINT2PTR(addr)); + + struct csp_cmp_message msg; + msg.poke.addr = csp_hton32(addr); + msg.poke.len = len; + memcpy(msg.poke.data, data, CSP_CMP_POKE_MAX_LEN); + + int ret = csp_cmp_poke(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + printf("CSP error: %d\r\n", ret); + return gs_csp_error(ret); + } + + return GS_OK; +} + +static int cmd_cmp_clock(gs_command_context_t *ctx, uint32_t node, uint32_t timeout, const gs_timestamp_t * set) +{ + char tbuf[GS_CLOCK_ISO8601_BUFFER_LENGTH]; + struct csp_cmp_message msg; + memset(&msg, 0, sizeof(msg)); + + if (set) { + gs_clock_to_iso8601_string(set, tbuf, sizeof(tbuf)); + printf("Set time: %s (%" PRIu32 ".%09" PRIu32 " sec)\r\n", tbuf, set->tv_sec, set->tv_nsec); + msg.clock.tv_sec = csp_hton32(set->tv_sec); + msg.clock.tv_nsec = csp_hton32(set->tv_nsec); + } + + gs_timestamp_t t1, t2; + gs_clock_get_time(&t1); + int ret = csp_cmp_clock(node, timeout, &msg); + if (ret != CSP_ERR_NONE) { + return gs_csp_error(ret); + } + gs_clock_get_time(&t2); + + /* Calculate round-trip time */ + const int64_t rtt = ((uint64_t)t2.tv_sec * 1000000000 + t2.tv_nsec) - ((uint64_t)t1.tv_sec * 1000000000 + t1.tv_nsec); + + gs_timestamp_t timestamp; + timestamp.tv_sec = csp_ntoh32(msg.clock.tv_sec); + timestamp.tv_nsec = csp_ntoh32(msg.clock.tv_nsec); + + gs_clock_to_iso8601_string(×tamp, tbuf, sizeof(tbuf)); + printf("Get time: %s (%" PRIu32 ".%09" PRIu32 " sec)\r\n", tbuf, timestamp.tv_sec, timestamp.tv_nsec); + + /* Calculate time difference to local clock. This takes the round-trip + * into account, but assumes a symmetrical link */ + const int64_t remote = (uint64_t)timestamp.tv_sec * 1000000000 + timestamp.tv_nsec; + const int64_t local = (uint64_t)t1.tv_sec * 1000000000 + t1.tv_nsec + rtt / 2; + + const double diff = (remote - local) / 1000000.0; + printf("Remote is %f ms %s local time\r\n", fabs(diff), diff > 0 ? "ahead of" : "behind"); + + return GS_OK; +} + +static int cmd_cmp_clock_get(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + + uint32_t node; + if (gs_string_to_uint32(ctx->argv[1], &node) != GS_OK) { + return GS_ERROR_ARG; + } + + uint32_t timeout = 1000; + if (ctx->argc > 2) { + if (gs_string_to_uint32(ctx->argv[2], &timeout) != GS_OK) { + return GS_ERROR_ARG; + } + } + + return cmd_cmp_clock(ctx, node, timeout, NULL); +} + +static int cmd_cmp_clock_set(gs_command_context_t *ctx) +{ + if (ctx->argc < 3) { + return GS_ERROR_ARG; + } + + uint32_t node; + if (gs_string_to_uint32(ctx->argv[1], &node) != GS_OK) { + return GS_ERROR_ARG; + } + + gs_timestamp_t ts; + if (gs_clock_from_string(ctx->argv[2], &ts) != GS_OK) { + return GS_ERROR_ARG; + } + + uint32_t timeout = 1000; + if (ctx->argc > 3) { + if (gs_string_to_uint32(ctx->argv[3], &timeout) != GS_OK) { + return GS_ERROR_ARG; + } + } + + return cmd_cmp_clock(ctx, node, timeout, &ts); +} + +static int cmd_cmp_clock_sync(gs_command_context_t *ctx) +{ + if (ctx->argc < 2) { + return GS_ERROR_ARG; + } + + uint32_t node; + if (gs_string_to_uint32(ctx->argv[1], &node) != GS_OK) { + return GS_ERROR_ARG; + } + + uint32_t timeout = 1000; + if (ctx->argc > 2) { + if (gs_string_to_uint32(ctx->argv[2], &timeout) != GS_OK) { + return GS_ERROR_ARG; + } + } + + gs_timestamp_t ts; + gs_clock_get_time(&ts); + + return cmd_cmp_clock(ctx, node, timeout, &ts); +} + +static const gs_command_t GS_COMMAND_SUB cmp_clock_commands[] = { + { + .name = "get", + .help = "Get clock on ", + .usage = " [timeout]", + .handler = cmd_cmp_clock_get, + }, + { + .name = "set", + .help = "Set time of ", + .usage = " [timeout]", + .handler = cmd_cmp_clock_set, + }, + { + .name = "sync", + .help = "Sync/set time of to time of this node", + .usage = " [timeout]", + .handler = cmd_cmp_clock_sync, + } +}; + +static const gs_command_t GS_COMMAND_SUB cmp_commands[] = { + { + .name = "ident", + .help = "Node id", + .usage = "[node] [timeout]", + .handler = cmd_cmp_ident, + },{ + .name = "route_set", + .help = "Update table", + .usage = " ", + .handler = cmd_cmp_route_set, + },{ + .name = "ifc", + .help = "Remote IFC", + .usage = " [timeout]", + .handler = cmd_cmp_ifc, + },{ + .name = "peek", + .help = "Show remote memory", + .usage = " [timeout]", + .handler = cmd_cmp_peek, + },{ + .name = "poke", + .help = "Modify remote memory", + .usage = " [timeout]", + .handler = cmd_cmp_poke, + },{ + .name = "clock", + .help = "Get/set clock", + .chain = GS_COMMAND_INIT_CHAIN(cmp_clock_commands), + } +}; + +static const gs_command_t GS_COMMAND_ROOT csp_commands[] = { + { + .name = "ping", + .help = "csp: Ping", + .usage = "[node] [timeout] [size] [opt: r|x|h|c]", + .handler = cmd_ping, + },{ + .name = "rps", + .help = "csp: Remote ps", + .usage = "[node] [timeout]", + .handler = cmd_ps, + },{ + .name = "memfree", + .help = "csp: Memory free", + .usage = "[node] [timeout]", + .handler = cmd_memfree, + },{ + .name = "buffree", + .help = "csp: Buffer free", + .usage = "[node] [timeout]", + .handler = cmd_buf_free, + },{ + .name = "reboot", + .help = "csp: Reboot", + .usage = "", + .handler = cmd_reboot, + },{ + .name = "shutdown", + .help = "csp: Shutdown", + .usage = "", + .handler = cmd_shutdown, + },{ + .name = "uptime", + .help = "csp: Uptime", + .usage = "[node] [timeout]", + .handler = cmd_uptime, + },{ + .name = "cmp", + .help = "csp: Management", + .chain = GS_COMMAND_INIT_CHAIN(cmp_commands), + }, +#ifdef CSP_DEBUG + { + .name = "route", + .help = "csp: Show routing table", + .handler = cmd_csp_route_print_table, + },{ + .name = "ifc", + .help = "csp: Show interfaces", + .handler = cmd_csp_route_print_interfaces, + },{ + .name = "conn", + .help = "csp: Show connection table", + .handler = cmd_csp_conn_print_table, + }, +#endif +#if CSP_USE_RDP + { + .name = "rdpopt", + .help = "csp: Set RDP options", + .handler = cmd_csp_rdp_set_opt, + .usage = " " + }, +#endif +}; + +gs_error_t gs_csp_register_commands(void) +{ + return GS_COMMAND_REGISTER(csp_commands); +} diff --git a/gomspace/libgscsp/src/conn.c b/gomspace/libgscsp/src/conn.c new file mode 100644 index 00000000..05e6459e --- /dev/null +++ b/gomspace/libgscsp/src/conn.c @@ -0,0 +1,22 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include <../lib/libcsp/src/csp_conn.h> // internal libcsp header + +size_t gs_csp_conn_get_open(void) +{ + size_t open = 0; + size_t max_connections; + const csp_conn_t * connections = csp_conn_get_array(&max_connections); + if (connections) { + for (unsigned int i = 0; i < max_connections; ++i) { + if (connections[i].state != CONN_CLOSED) { + ++open; + } + } + } + + // csp_conn_print_table(); + + return open; +} diff --git a/gomspace/libgscsp/src/csp.c b/gomspace/libgscsp/src/csp.c new file mode 100644 index 00000000..2367ec6a --- /dev/null +++ b/gomspace/libgscsp/src/csp.c @@ -0,0 +1,91 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include "local.h" + +void gs_csp_conf_get_defaults_embedded(gs_csp_conf_t * conf) +{ + static const gs_csp_conf_t defaults = { + .use_gs_log = true, + .use_command_line_options = true, + .csp_buffer_size = 256, // typical MTU size is 256 + .csp_buffers = 10, // in case of RDP connections, must be > RDP Windows size + .address = 1, + .hostname = "hostname", + .model = "model", + .revision = "revision", + }; + + *conf = defaults; + +#if GS_CSP_COMMAND_LINE_SUPPORT + conf->address = gs_csp_command_line_get_address(); +#endif +} + +void gs_csp_conf_get_defaults_server(gs_csp_conf_t * conf) +{ + gs_csp_conf_get_defaults_embedded(conf); + conf->csp_buffer_size = 512; + conf->csp_buffers = 400; +} + +gs_error_t gs_csp_init(const gs_csp_conf_t * conf) +{ + GS_CHECK_ARG(conf != NULL); + + if (conf->use_gs_log) { + gs_csp_log_init(); + } + + int res = csp_buffer_init(conf->csp_buffers, conf->csp_buffer_size); + if (res != CSP_ERR_NONE) { + log_error("%s: csp_buffer_init(buffers: %u, size: %u) failed, CSP error: %d, error: %d", + __FUNCTION__, (unsigned int) conf->csp_buffers, (unsigned int) conf->csp_buffer_size, res, gs_csp_error(res)); + return gs_csp_error(res); + } + + csp_set_hostname(conf->hostname); + csp_set_model(conf->model); + csp_set_revision(conf->revision); + + uint8_t csp_address = conf->address; +#if GS_CSP_COMMAND_LINE_SUPPORT + if (gs_csp_command_line_is_address_set()) { + csp_address = gs_csp_command_line_get_address(); + } +#endif + + res = csp_init(csp_address); + if (res != CSP_ERR_NONE) { + log_error("%s: csp_init(address: %u) failed, CSP error: %d, error: %d", + __FUNCTION__, conf->address, res, gs_csp_error(res)); + return gs_csp_error(res); + } + +#if GS_CSP_COMMAND_LINE_SUPPORT + if (conf->use_command_line_options) { + gs_error_t error = gs_csp_command_line_configure_interfaces(); + if (error) { + log_error("%s: gs_csp_command_line_configure_interfaces() failed, error: %d", + __FUNCTION__, error); + return error; + } + } +#endif + + return GS_OK; +} + +bool gs_csp_is_address_valid(uint8_t address) +{ + if (address < 1) { + return false; + } + if (address >= 33) { + return false; + } + return true; +} diff --git a/gomspace/libgscsp/src/drivers/can/can.c b/gomspace/libgscsp/src/drivers/can/can.c new file mode 100644 index 00000000..965977ca --- /dev/null +++ b/gomspace/libgscsp/src/drivers/can/can.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NO_OF_CAN_CHANNELS 2 +#define MAX_NAME_LENGTH 10 // It says in csp_types.h, that it should be below 10 bytes + +// change default log group +#define LOG_DEFAULT gs_can_log + +typedef struct { + // self reference device handle + uint8_t can_ch; + // CSP interface name + char interface_name[MAX_NAME_LENGTH]; + // CSP interface + csp_iface_t interface; +} gs_csp_can_interface_t; + +static gs_csp_can_interface_t csp_can_interfaces[NO_OF_CAN_CHANNELS]; + +static void gs_csp_can_rxdata_callback_isr(int hdl, + uint32_t canMsgId, + bool extendedMsgId, + const void * data, + size_t data_size, + uint32_t nowMs, + void * user_data, + gs_context_switch_t * cswitch) +{ + csp_can_rx(&csp_can_interfaces[hdl].interface, canMsgId, data, data_size, &cswitch->task_woken); +} + +// Required by libcsp +int csp_can_tx_frame(csp_iface_t *interface, uint32_t id, const uint8_t * data, uint8_t dlc) +{ + return gs_can_send_extended(((gs_csp_can_interface_t *)interface->driver)->can_ch, id, data, dlc, 1000); +} + +gs_error_t gs_csp_can_init2(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, bool set_default_route, csp_iface_t ** csp_if) +{ + GS_CHECK_HANDLE(device < NO_OF_CAN_CHANNELS); + gs_csp_can_interface_t * interface = &csp_can_interfaces[device]; + + // Register/subscribe to CAN frames for CSP + const uint32_t can_id = CFP_MAKE_DST(csp_get_address()); + const uint32_t can_mask = CFP_MAKE_DST((1 << CFP_HOST_SIZE) - 1); + + log_debug("%s(%u): id=0x%" PRIx32 ", mask=0x%" PRIx32, __FUNCTION__, device, can_id, can_mask); + + if (gs_string_empty(name)) { + name = GS_CSP_CAN_DEFAULT_IF_NAME; + } + if (strlen(name) >= MAX_NAME_LENGTH) { + return GS_ERROR_ARG; + } + + if (csp_iflist_get_by_name(name)) { + log_error("%s(%u): name: [%s] - already exists", __FUNCTION__, device, name); + return GS_ERROR_EXIST; + } + + // hook CAN into CSP + GS_STRNCPY(interface->interface_name, name); + interface->interface.name = interface->interface_name; + interface->interface.nexthop = csp_can_tx; + interface->interface.mtu = mtu; + interface->interface.driver = interface; + + csp_iflist_add(&interface->interface); + + if (csp_if) { + *csp_if = &interface->interface; + } + + gs_error_t error = gs_can_set_extended_filter_mask(0, can_id, can_mask, gs_csp_can_rxdata_callback_isr, NULL); + if (error) { + log_error("%s: gs_can_set_extended_filter_mask() failed, error: %s", __FUNCTION__, gs_error_string(error)); + return error; + } + + error = gs_can_start(device); + if (error) { + log_error("%s: gs_can_start() failed, error: %s", __FUNCTION__, gs_error_string(error)); + return error; + } + + if (set_default_route) { + // Route all to CAN + csp_rtable_set(0, 0, &interface->interface, CSP_NODE_MAC); + } + + return GS_OK; +} + +gs_error_t gs_csp_can_init(uint8_t device, uint8_t csp_addr, uint32_t mtu, const char * name, csp_iface_t ** csp_if) +{ + return gs_csp_can_init2(device, csp_addr, mtu, name, true, csp_if); +} diff --git a/gomspace/libgscsp/src/drivers/i2c/i2c.c b/gomspace/libgscsp/src/drivers/i2c/i2c.c new file mode 100644 index 00000000..c40cd9c8 --- /dev/null +++ b/gomspace/libgscsp/src/drivers/i2c/i2c.c @@ -0,0 +1,77 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#if !defined(__linux__) +#include +#endif + +#define E_FAIL -19 // The CSP I2C driver evaluates any other value than -1 as fail + +#define I2C_FRAME_OVERHEAD (sizeof(i2c_frame_t) - sizeof(((i2c_frame_t *)0)->data)) + +static void gs_csp_i2c_rxdata_callback_isr(uint8_t handle, const uint8_t * rx, size_t rx_length, gs_context_switch_t * cswitch) +{ + i2c_frame_t * frame = (i2c_frame_t *) (rx - I2C_FRAME_OVERHEAD); + frame->len = rx_length; +#if (__linux__) + csp_i2c_rx(frame, NULL); +#else + csp_i2c_rx(frame, &cswitch->task_woken); +#endif +} + +static void * gs_csp_i2c_get_buffer(uint8_t handle) +{ + void * buff = csp_buffer_get_isr(I2C_MTU); + if (buff != NULL) { + buff = ((uint8_t *)buff) + I2C_FRAME_OVERHEAD; + } + return buff; +} + +/** + CSP send function, required by libcsp + */ +int i2c_send(int handle, i2c_frame_t * frame, uint16_t timeout) +{ + int res_tx = gs_i2c_master_transaction(handle, frame->dest, frame->data, frame->len, 0, 0, timeout); + if (res_tx == GS_OK) { + csp_buffer_free(frame); + return E_NO_ERR; + } else { + return E_FAIL; + } +} + +/** + CSP init function, required by libcsp + */ +int i2c_init(int handle, int mode, uint8_t addr, uint16_t speed, int queue_len_tx, int queue_len_rx, + i2c_callback_t callback) +{ + if (gs_i2c_slave_set_rx(handle, gs_csp_i2c_rxdata_callback_isr) != GS_OK) { + return E_FAIL; + } + if (gs_i2c_slave_set_get_rx_buf(handle, gs_csp_i2c_get_buffer, I2C_MTU) != GS_OK) { + return E_FAIL; + } + if (gs_i2c_slave_start(handle) != GS_OK) { + return E_FAIL; + } + return E_NO_ERR; +} + +gs_error_t gs_csp_i2c_init(uint8_t device, uint8_t csp_addr) +{ + int dummy_speed = 0; // Speed not used + + /* Calls CSP I2C init, which has the I2C interface instance + From here the above "i2c_init" is called */ + return gs_csp_error(csp_i2c_init(csp_addr, device, dummy_speed)); +} diff --git a/gomspace/libgscsp/src/drivers/kiss/kiss.c b/gomspace/libgscsp/src/drivers/kiss/kiss.c new file mode 100644 index 00000000..c3357f7d --- /dev/null +++ b/gomspace/libgscsp/src/drivers/kiss/kiss.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include + +static csp_iface_t csp_if_kiss; +static uint8_t uart_csp_device; + +static void usart_rx_callback(void * user_data, const uint8_t * data, size_t data_size, gs_context_switch_t * cswitch) +{ + csp_kiss_rx(&csp_if_kiss, (uint8_t *)data, data_size, cswitch); +} + +static void csp_kiss_putc(char c) +{ + gs_uart_write(uart_csp_device, -1, c); +} + +static void csp_kiss_discard(char c, void *pxTaskWoken) +{ + // Do nothing with discarded characters +} + +gs_error_t gs_csp_kiss_init(uint8_t device) +{ + static csp_kiss_handle_t csp_kiss_driver; + static const char * kiss_name = "KISS"; + csp_route_set(CSP_DEFAULT_ROUTE, &csp_if_kiss, CSP_NODE_MAC); + + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, csp_kiss_putc, csp_kiss_discard, kiss_name); + + uart_csp_device = device; + + return gs_uart_set_rx_callback(device, usart_rx_callback, NULL); +} diff --git a/gomspace/libgscsp/src/error.c b/gomspace/libgscsp/src/error.c new file mode 100644 index 00000000..45777e31 --- /dev/null +++ b/gomspace/libgscsp/src/error.c @@ -0,0 +1,54 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +gs_error_t gs_csp_error(int csp_error) +{ + switch (csp_error) { + case CSP_ERR_NONE: /* No error */ + return GS_OK; + + case CSP_ERR_NOMEM: /* Not enough memory */ + return GS_ERROR_ALLOC; + + case CSP_ERR_INVAL: /* Invalid argument */ + return GS_ERROR_ARG; + + case CSP_ERR_TIMEDOUT: /* Operation timed out */ + return GS_ERROR_TIMEOUT; + + case CSP_ERR_USED: /* Resource already in use */ + return GS_ERROR_IN_USE; + + case CSP_ERR_NOTSUP: /* Operation not supported */ + return GS_ERROR_NOT_SUPPORTED; + + case CSP_ERR_BUSY: /* Device or resource busy */ + return GS_ERROR_BUSY; + + case CSP_ERR_ALREADY: /* Connection already in progress */ + return GS_ERROR_ALREADY_IN_PROGRESS; + + case CSP_ERR_RESET: /* Connection reset */ + return GS_ERROR_CONNECTION_RESET; + + case CSP_ERR_NOBUFS: /* No more buffer space available */ + return GS_ERROR_NO_BUFFERS; + + case CSP_ERR_TX: /* Transmission failed */ + case CSP_ERR_DRIVER: /* Error in driver layer */ + return GS_ERROR_IO; + + case CSP_ERR_AGAIN: + return GS_ERROR_AGAIN; + + case CSP_ERR_HMAC: /* HMAC failed */ + case CSP_ERR_XTEA: /* XTEA failed */ + case CSP_ERR_CRC32: /* CRC32 failed */ + return GS_ERROR_DATA; + + default: + break; + } + return csp_error; +} diff --git a/gomspace/libgscsp/src/freertos/cpu.c b/gomspace/libgscsp/src/freertos/cpu.c new file mode 100644 index 00000000..a17fd62b --- /dev/null +++ b/gomspace/libgscsp/src/freertos/cpu.c @@ -0,0 +1,8 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include + +void cpu_reset(void) +{ + gs_sys_reset(GS_SYS_RESET_CSP); +} diff --git a/gomspace/libgscsp/src/linux/command_line.c b/gomspace/libgscsp/src/linux/command_line.c new file mode 100644 index 00000000..7c2b9bec --- /dev/null +++ b/gomspace/libgscsp/src/linux/command_line.c @@ -0,0 +1,265 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include "../local.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IF_NAME "if" + +#define DEFAULT_CAN_DEVICE "can0" + +#define DEFAULT_KISS_IF_NAME "KISS" +#define DEFAULT_KISS_DEVICE "/dev/ttyUSB0" +#define KISS_SPEED "speed" +#define DEFAULT_KISS_SPEED 500000 + +#define DEFAULT_ZMQ_SERVER "localhost" + +#define DEFAULT_I2C_DEVICE "0" + +#define CSP_ADDRESS_NOT_SET 255 +#define DEFAULT_CSP_ADDRESS 8 + +static uint8_t csp_address = CSP_ADDRESS_NOT_SET; +static const char * csp_can_device = NULL; +static const char * csp_kiss_device = NULL; +static const char * csp_i2c_device = NULL; +static const char * csp_zmq_server = NULL; +static const char * csp_rtable = NULL; + +static int parser(int key, char *arg, struct argp_state *state) +{ +switch (key) { + case 'a': + return gs_string_to_uint8(arg, &csp_address); + + case 'c': + if (csp_can_device) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_can_device = arg; + } else { + csp_can_device = DEFAULT_CAN_DEVICE; + } + break; + + case 'k': + if (csp_kiss_device) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_kiss_device = arg; + } else { + csp_kiss_device = DEFAULT_KISS_DEVICE; + } + break; + + case 'i': + if (csp_i2c_device) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_i2c_device = arg; + } else { + csp_i2c_device = DEFAULT_I2C_DEVICE; + } + break; + + case 'z': + if (csp_zmq_server) { + return GS_ERROR_IN_USE; + } + if (arg) { + csp_zmq_server = arg; + } else { + csp_zmq_server = DEFAULT_ZMQ_SERVER; + } + break; + + case 'R': + csp_rtable = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static const struct argp_option options[] = { + { + .name = "csp-address", + .key = 'a', + .arg = "ADDR", + .flags = 0, + .doc = "Set address, default: " GS_DEF2STRING(DEFAULT_CSP_ADDRESS) + }, + { + .name = "csp-rtable", + .key = 'R', + .arg = "RTABLE", + .flags = 0, + .doc = "Set routing table\nRTABLE=
/ [mac]\nExample: \"0/0 ZMQHUB 24, 24/2 ZMQHUB\"" + }, +#if (CSP_USE_CAN) + { + .name = "csp-can", + .key = 'c', + .arg = "DEVICE", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add CAN interface\nDEVICE=" DEFAULT_CAN_DEVICE + }, +#endif +#if (CSP_USE_KISS) + { + .name = "csp-kiss", + .key = 'k', + .arg = "DEVICE", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add KISS over UART interface\nDEVICE=" DEFAULT_KISS_DEVICE "," IF_NAME "=" DEFAULT_KISS_IF_NAME","KISS_SPEED"=" GS_DEF2STRING(DEFAULT_KISS_SPEED) + }, +#endif +#if (CSP_USE_I2C) + { + .name = "csp-i2c", + .key = 'i', + .arg = "DEVICE", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add I2C interface\nDEVICE=0,"GS_I2C_COMMAND_LINE_SPEED"=" GS_DEF2STRING(GS_I2C_DEFAULT_BPS) "," GS_I2C_COMMAND_LINE_ADDRESS "=1," GS_I2C_COMMAND_LINE_DEVICE "=" GS_DEF2STRING(GS_I2C_ALL_DEVICES) + }, +#endif +#if (CSP_USE_ZMQHUB) + { + .name = "csp-zmq", + .key = 'z', + .arg = "SERVER", + .flags = OPTION_ARG_OPTIONAL, + .doc = "Add ZMQ interface\nSERVER=" DEFAULT_ZMQ_SERVER + }, +#endif + { + .flags = OPTION_DOC, + .name = "Examples:" +#if (CSP_USE_CAN) + "\n CAN: configure address 10 and CAN interface can0:" + "\n $ -a10 -ccan0" +#endif +#if (CSP_USE_KISS) + "\n KISS: configure address 10 and uart on /dev/ttyUSB0 at baudrate 500000:" + "\n $ -a10 -k/dev/ttyUSB0,speed=500000" +#endif +#if (CSP_USE_I2C) + "\n I2C: configure address 10 and I2C Aardvark dongle with id 2238384015, speed 400K:" + "\n $ -a10 -i2238384015,speed=400000" +#endif +#if (CSP_USE_ZMQHUB) + "\n ZMQ: configure address 10 and ZMQ proxy on localhost:" + "\n $ -a10 -zlocalhost" +#endif + }, + {0} +}; + +static const struct argp argp = {.options = options, .parser = parser}; + +const struct argp_child gs_csp_command_line_options = {.argp = &argp, .header = "CSP"}; + +gs_error_t gs_csp_command_line_configure_interfaces(void) +{ +#if (CSP_USE_KISS) + // KISS - only here, because the embedded init functions are stubbed in libemul + if (csp_kiss_device) { + static char device[50]; + static char ifname[50]; + uint32_t speed; + int res = gs_string_get_suboption_string(csp_kiss_device, NULL, DEFAULT_KISS_DEVICE, device, sizeof(device)); + res |= gs_string_get_suboption_string(csp_kiss_device, IF_NAME, DEFAULT_KISS_IF_NAME, ifname, sizeof(ifname)); + res |= gs_string_get_suboption_uint32(csp_kiss_device, KISS_SPEED, DEFAULT_KISS_SPEED, &speed); + if (res == GS_OK) { + static csp_iface_t csp_if_kiss; + static csp_kiss_handle_t csp_kiss_driver; + csp_kiss_init(&csp_if_kiss, &csp_kiss_driver, usart_putc, usart_insert, ifname); + struct usart_conf conf = {.device = device, .baudrate = speed}; + usart_init(&conf); + void my_usart_rx(uint8_t * buf, int len, void * pxTaskWoken) { + csp_kiss_rx(&csp_if_kiss, buf, len, pxTaskWoken); + } + usart_set_callback(my_usart_rx); + } + } +#endif + +#if (CSP_USE_CAN) + // CAN - only here, because the embedded init functions are stubbed in libemul + if (csp_can_device) { + char device[50]; + int res = gs_string_get_suboption_string(csp_can_device, NULL, DEFAULT_CAN_DEVICE, device, sizeof(device)); + if (res == GS_OK) { + csp_can_socketcan_init(device, 0, 0); + } + } +#endif + +#if (CSP_USE_ZMQHUB) + // ZMQ - currently ZMQ is only supported on Linux, and therefor handled here + if (csp_zmq_server) { + char server[50]; + int res = gs_string_get_suboption_string(csp_zmq_server, NULL, DEFAULT_ZMQ_SERVER, server, sizeof(server)); + if (res == GS_OK) { + csp_zmqhub_init(csp_get_address(), server); + } + } +#endif + +#if (CSP_USE_I2C) + // I2C + if (csp_i2c_device) { + uint8_t device = 0; + gs_string_get_suboption_uint8(csp_i2c_device, GS_I2C_COMMAND_LINE_DEVICE, GS_I2C_ALL_DEVICES, &device); + if (device == GS_I2C_ALL_DEVICES) { + device = 0; + } + + char modified_options[300]; + snprintf(modified_options, sizeof(modified_options), "%s,%s=%u", csp_i2c_device, GS_I2C_COMMAND_LINE_ADDRESS, csp_get_address()); + gs_error_t error = gs_function_invoke("i2c", modified_options); + if (error) { + log_error("Failed to initialize I2C adapter, error: %s", gs_error_string(error)); + } else { + error = gs_csp_i2c_init(device, csp_get_address()); + if (error) { + log_error("gs_csp_i2c_init(%u, %u) failed, error: %s", device, csp_get_address(), gs_error_string(error)); + } + } + } +#endif + + return GS_OK; +} + +bool gs_csp_command_line_is_address_set(void) +{ + return (csp_address != CSP_ADDRESS_NOT_SET); +} + +uint8_t gs_csp_command_line_get_address(void) +{ + if (gs_csp_command_line_is_address_set()) { + return csp_address; + } + return DEFAULT_CSP_ADDRESS; +} + +const char * gs_csp_command_line_get_rtable(void) +{ + return csp_rtable; +} diff --git a/gomspace/libgscsp/src/local.h b/gomspace/libgscsp/src/local.h new file mode 100644 index 00000000..5c033c14 --- /dev/null +++ b/gomspace/libgscsp/src/local.h @@ -0,0 +1,21 @@ +#ifndef GS_CSP_SRC_LOCAL_H +#define GS_CSP_SRC_LOCAL_H +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#if (__linux__) +#define GS_CSP_COMMAND_LINE_SUPPORT 1 +#include +#endif + +GS_LOG_GROUP_EXTERN(gs_csp_log); +#define LOG_DEFAULT gs_csp_log + +// local command line APIs +bool gs_csp_command_line_is_address_set(void); +uint8_t gs_csp_command_line_get_address(void); +const char * gs_csp_command_line_get_rtable(void); +gs_error_t gs_csp_command_line_configure_interfaces(void); + +#endif diff --git a/gomspace/libgscsp/src/log.c b/gomspace/libgscsp/src/log.c new file mode 100644 index 00000000..932e5394 --- /dev/null +++ b/gomspace/libgscsp/src/log.c @@ -0,0 +1,64 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include "local.h" + +GS_LOG_GROUP(gs_csp_log, "csp", GS_LOG_CAT_CSP, LOG_DEFAULT_MASK); + +static void gs_log_csp_debug_hook(csp_debug_level_t level, const char *format, va_list args) +{ + gs_log_level_t mapped_level; + + switch (level) { + /* Regular log levels */ + default: + case CSP_ERROR: + mapped_level = LOG_ERROR; + break; + case CSP_WARN: + mapped_level = LOG_WARNING; + break; + case CSP_INFO: + mapped_level = LOG_INFO; + break; + /* Extended log levels */ + case CSP_BUFFER: + mapped_level = LOG_TRACE; + break; + case CSP_PACKET: + mapped_level = LOG_INFO; + break; + case CSP_PROTOCOL: + mapped_level = LOG_DEBUG; + break; + case CSP_LOCK: + mapped_level = LOG_TRACE; + break; + } + + const int do_log = ((LOG_DEFAULT->mask & (1 << mapped_level)) > 0); + + if (do_log) { + /* forward to log system */ + gs_log_va(mapped_level, LOG_DEFAULT, format, args); + } +} + +gs_error_t gs_csp_log_init(void) +{ + gs_log_group_register(LOG_DEFAULT); + + csp_debug_set_level(CSP_ERROR, true); + csp_debug_set_level(CSP_WARN, true); + csp_debug_set_level(CSP_INFO, true); + csp_debug_set_level(CSP_BUFFER, true); + csp_debug_set_level(CSP_PACKET, true); + csp_debug_set_level(CSP_PROTOCOL, true); + csp_debug_set_level(CSP_LOCK, true); + + csp_debug_hook_set(gs_log_csp_debug_hook); + + return GS_OK; +} diff --git a/gomspace/libgscsp/src/router.c b/gomspace/libgscsp/src/router.c new file mode 100644 index 00000000..95f013eb --- /dev/null +++ b/gomspace/libgscsp/src/router.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include "../lib/libcsp/src/csp_qfifo.h" // internal libcsp header -> FIFO_TIMEOUT, csp_qfifo_wake_up() +#include <../lib/libcsp/src/csp_conn.h> // internal libcsp header +#include "local.h" + +typedef struct { + bool run; + gs_thread_t thread; +} gs_csp_router_t; + +static gs_csp_router_t gs_csp_router; + +static void * gs_csp_router_task(void *param) +{ + /* Here there be routing */ + while (gs_csp_router.run) { + csp_route_work(FIFO_TIMEOUT); + } + log_info("CSP router task terminating"); + gs_thread_exit(0); +} + +gs_error_t gs_csp_router_task_stop(void) +{ + GS_CHECK_HANDLE(gs_csp_router.run && (gs_csp_router.thread != 0)); + + // Close connections in state "RDP closing" - instead of waiting for timeout +#ifdef CSP_USE_RDP + { + size_t max_connections; + const csp_conn_t * conn = csp_conn_get_array(&max_connections); + if (conn && max_connections) { + for (unsigned int i = 0; i < max_connections; ++i, ++conn) { + if ((conn->state == CONN_OPEN) && (conn->rdp.state == RDP_CLOSE_WAIT)) { + log_info("Force close RDP %p in state closing", conn); + csp_close((csp_conn_t *) conn); + } + } + } + } +#endif + + // wait for RDP connections to close + unsigned int open = gs_csp_conn_get_open(); + if (open) { + const unsigned int MAX_TIMEOUT_MS = 30000; + log_info("Waiting up to %u mS for %u connection(s) to timeout/close ...", MAX_TIMEOUT_MS, open); + const uint32_t start_ms = gs_time_rel_ms(); + while (gs_csp_conn_get_open() && (gs_time_diff_ms(start_ms, gs_time_rel_ms()) < MAX_TIMEOUT_MS)) { + gs_time_sleep_ms(200); + } + } + + log_info("Waiting for CSP router task to stop ..."); + gs_csp_router.run = false; + csp_qfifo_wake_up(); + gs_error_t error = gs_thread_join(gs_csp_router.thread, NULL); + memset(&gs_csp_router, 0, sizeof(gs_csp_router)); + log_info("CSP router task stopped"); + return error; +} + +gs_error_t gs_csp_router_task_start(size_t stack_size, gs_thread_priority_t priority) +{ + if (gs_csp_router.run) { + return GS_ERROR_IN_USE; + } + + gs_csp_router.run = true; + gs_error_t error = gs_thread_create("RTE", gs_csp_router_task, NULL, stack_size, priority, + GS_THREAD_CREATE_JOINABLE, &gs_csp_router.thread); + if (error) { + memset(&gs_csp_router, 0, sizeof(gs_csp_router)); + } + return error; +} diff --git a/gomspace/libgscsp/src/rtable.c b/gomspace/libgscsp/src/rtable.c new file mode 100644 index 00000000..f836e3d0 --- /dev/null +++ b/gomspace/libgscsp/src/rtable.c @@ -0,0 +1,69 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include "local.h" + +// Return interface, if only one configured +static csp_iface_t * get_single_if(unsigned int * return_count) +{ + unsigned int count = 0; + csp_iface_t * found = NULL; + + for (csp_iface_t * ifc = csp_iflist_get(); ifc; ifc = ifc->next) { + if (strcasecmp(ifc->name, "LOOP") == 0) { + // ignore loopback + } else { + ++count; + found = ifc; + } + } + *return_count = count; + return found; +} + +gs_error_t gs_csp_rtable_load(const char * rtable, bool set_default_route, bool use_command_line_option) +{ + //csp_rtable_clear(); + +#if GS_CSP_COMMAND_LINE_SUPPORT + if (use_command_line_option && gs_string_empty(rtable)) { + // try rtable from command line (if set) + rtable = gs_csp_command_line_get_rtable(); + } +#endif + + if (gs_string_empty(rtable) == false) { + + if (csp_rtable_check(rtable) > 0) { + csp_rtable_load(rtable); + log_info("%s: loaded routing table [%s]", __FUNCTION__, rtable); + return GS_OK; + } + + log_warning("%s: ignoring route table: [%s] due to error(s)", __FUNCTION__, rtable); + } + + if (set_default_route) { + unsigned int count = 0; + csp_iface_t * ifc = get_single_if(&count); + if (count == 0) { + log_warning("%s: no interfaces configured", __FUNCTION__); + return GS_ERROR_NOT_FOUND; + } + + if (count > 1) { + log_warning("%s: %u interfaces configured - will not set default routes", __FUNCTION__, count); + return GS_ERROR_AMBIGUOUS; + } + + // set default route + int res = csp_route_set(CSP_DEFAULT_ROUTE, ifc, CSP_NODE_MAC); + if (res != CSP_ERR_NONE) { + log_warning("%s: failed to set default route on interface: [%s], CSP error: %d", __FUNCTION__, ifc->name, res); + return gs_csp_error(res); + } + } + + return GS_OK; +} diff --git a/gomspace/libgscsp/src/service_dispatcher.c b/gomspace/libgscsp/src/service_dispatcher.c new file mode 100644 index 00000000..507549af --- /dev/null +++ b/gomspace/libgscsp/src/service_dispatcher.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include +#include +#include "../lib/libcsp/src/csp_conn.h" // internal libcsp header +#include "local.h" + +static GS_LOG_GROUP(gs_cspdispatcher_log, "cspdispatcher", GS_LOG_CAT_CSP, LOG_DEFAULT_MASK); +#undef LOG_DEFAULT +#define LOG_DEFAULT gs_cspdispatcher_log + +#define WD_TIMEOUT_SECOUNDS 30 +#define DEFAULT_TIMEOUT_MS (((WD_TIMEOUT_SECOUNDS * 1000) / 3) * 2) + +struct gs_csp_service_dispatcher { + // Configuration + const gs_csp_service_dispatcher_conf_t * conf; + // Run or stop/exit. + bool run; + // Server socket + csp_socket_t * socket; + // Software watchdog + gs_swwd_hdl_t * wd; + // Thread handle. + gs_thread_t thread; +}; + +static void * service_dispatcher_task(void * parameter) +{ + gs_csp_service_dispatcher_t handle = parameter; + + unsigned int timeout_ms = (handle->conf->callback) ? 0 : DEFAULT_TIMEOUT_MS; + + log_debug("[%s] entering connection loop, timeout: %u mS", handle->conf->name, timeout_ms); + + while (handle->run) { + if (handle->wd) { + gs_swwd_touch(handle->wd); + } + + /* Wait for incoming connection, or timeout */ + csp_conn_t * conn = csp_accept(handle->socket, timeout_ms); + + if (conn) { + unsigned int in_port = csp_conn_dport(conn); + + log_debug("[%s] new connection %p on port: %u, source: %d:%d, flags: 0x%x", + handle->conf->name, conn, in_port, csp_conn_src(conn), csp_conn_sport(conn), csp_conn_flags(conn)); + + gs_csp_service_handler_t handler; + if (in_port < handle->conf->handler_array_size) { + handler = handle->conf->handler_array[in_port]; + } else { + handler = NULL; + } + + if (handler) { + gs_error_t error = (handler)(conn); + log_debug("[%s] connection on port: %u processed by %p, error: %s", + handle->conf->name, in_port, handler, gs_error_string(error)); + } else { + log_warning("[%s] no handler on port: %u - closing connection: source: %d:%d, flags: 0x%x", + handle->conf->name, in_port, csp_conn_src(conn), csp_conn_sport(conn), csp_conn_flags(conn)); + csp_close(conn); + } + } + + if (handle->conf->callback) { + timeout_ms = handle->conf->callback(); + if (timeout_ms > DEFAULT_TIMEOUT_MS) { + timeout_ms = DEFAULT_TIMEOUT_MS; + } + } + } + + log_debug("[%s] terminating ...", handle->conf->name); + gs_thread_exit(NULL); +} + +static gs_error_t service_dispatcher_create(const gs_csp_service_dispatcher_conf_t * conf, + size_t stack_size, + gs_thread_priority_t priority, + gs_csp_service_dispatcher_t * return_handle) +{ + gs_csp_service_dispatcher_t handle = calloc(1, sizeof(*handle)); + if (handle == 0) { + return GS_ERROR_ALLOC; + } + + *return_handle = handle; + + handle->conf = conf; + + // Create watchdog + if (conf->disable_watchdog == false) { + gs_error_t error = gs_swwd_register(&handle->wd, WD_TIMEOUT_SECOUNDS, NULL, NULL, conf->name); + if (error) { + log_error("[%s] gs_swwd_register(%s, %u) failed, error: %d", + conf->name, conf->name, WD_TIMEOUT_SECOUNDS, error); + handle->wd = NULL; + return error; + } + } + + // Open "server" socket + handle->socket = csp_socket(conf->socket_options); + if (handle->socket == NULL) { + log_error("[%s] csp_socket(0) failed", + conf->name); + return GS_ERROR_ALLOC; + } + + // Bind to port(s) to socket + for (unsigned int i = 0; i < conf->handler_array_size; ++i) { + if (conf->handler_array[i]) { + int res = csp_bind(handle->socket, i); + if (res) { + log_error("[%s] csp_bind(socket: %p, port: %u) failed, result: %d", + conf->name, handle->socket, i, res); + return GS_ERROR_IN_USE; + } + } + } + + // Bind on "any" port? + if (conf->bind_any) { + int res = csp_bind(handle->socket, CSP_ANY); + if (res) { + log_error("[%s] csp_bind(socket: %p, port: %u) failed, result: %d", + conf->name, handle->socket, CSP_ANY, res); + return GS_ERROR_IN_USE; + } + } + + // Create listen backlog + { + size_t backlog = conf->listen_backlog ? conf->listen_backlog : 10; + int res = csp_listen(handle->socket, backlog); + if (res) { + log_error("[%s] csp_listen(%p, %zu) failed, result: %d", + conf->name, handle->socket, backlog, res); + return GS_ERROR_UNKNOWN; + } + } + + // Launch thread + handle->run = true; + gs_error_t error = gs_thread_create(handle->conf->name, service_dispatcher_task, handle, stack_size, priority, + GS_THREAD_CREATE_JOINABLE, &handle->thread); + if (error) { + handle->thread = 0; + } + + return error; +} + +gs_error_t gs_csp_service_dispatcher_create(const gs_csp_service_dispatcher_conf_t * conf, + size_t stack_size, + gs_thread_priority_t priority, + gs_csp_service_dispatcher_t * return_handle) +{ + GS_CHECK_ARG(conf != NULL); + + gs_log_group_register(gs_cspdispatcher_log); + + gs_csp_service_dispatcher_t handle; + gs_error_t error = service_dispatcher_create(conf, stack_size, priority, &handle); + if (error) { + //gs_csp_service_dispatcher_destroy(handle); + handle = NULL; + } + + if (return_handle) { + *return_handle = handle; + } + + return error; +} + +gs_error_t gs_csp_service_dispatcher_wake_up(gs_csp_service_dispatcher_t handle) +{ + GS_CHECK_HANDLE(handle && handle->socket && handle->socket->socket); + csp_packet_t * packet = NULL; + int res = csp_queue_enqueue(handle->socket->socket, &packet, 0); + return (res == CSP_QUEUE_OK) ? GS_OK : GS_ERROR_FULL; +} + +gs_error_t gs_csp_service_dispatcher_destroy(gs_csp_service_dispatcher_t handle) +{ + GS_CHECK_HANDLE(handle && handle->conf); + + log_debug("[%s] stopping dispatcher ...", handle->conf->name); + + handle->run = false; + if (handle->thread) { + gs_csp_service_dispatcher_wake_up(handle); + gs_thread_join(handle->thread, NULL); + } + + csp_close(handle->socket); + + if (handle->wd) { + gs_swwd_deregister(&handle->wd); + } + + memset(handle, 0, sizeof(*handle)); + free(handle); + + return GS_OK; +} diff --git a/gomspace/libgscsp/src/service_handler.c b/gomspace/libgscsp/src/service_handler.c new file mode 100644 index 00000000..b602847d --- /dev/null +++ b/gomspace/libgscsp/src/service_handler.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ + +#include +#include +#include +#include + +// callback for processing packets on a connection. +typedef void (*csp_service_packet_handler_t)(csp_conn_t * conn, csp_packet_t * packet); + +// Process all packets on the connectio and close it when done. +static gs_error_t call_csp_packet_handler(csp_conn_t * conn, csp_service_packet_handler_t handler) +{ + csp_packet_t *packet; + while ((packet = csp_read(conn, 0))) { + (handler)(conn, packet); + } + csp_close(conn); + return GS_OK; +} + +gs_error_t gs_csp_cmp_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +gs_error_t gs_csp_ping_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +gs_error_t gs_csp_ps_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +static void memfree(csp_conn_t * conn, csp_packet_t * packet) +{ + uint32_t mem_free = 0; + + gs_mem_ram_type_t ram_type = gs_mem_get_ram_default(); + gs_mem_ram_stat_t ram_stat; + if(gs_mem_get_ram_stat(ram_type, &ram_stat) == GS_OK) { + mem_free = ram_stat.available; + } + + mem_free = util_hton32(mem_free); + memcpy(packet->data, &mem_free, sizeof(mem_free)); + packet->length = sizeof(mem_free); + + if (!csp_send(conn, packet, 0)) { + csp_buffer_free(packet); + } +} + +gs_error_t gs_csp_mem_free_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, memfree); +} + +gs_error_t gs_csp_reboot_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +gs_error_t gs_csp_buf_free_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, csp_service_handler); +} + +static void uptime(csp_conn_t * conn, csp_packet_t * packet) +{ + uint32_t time = gs_time_uptime(); + time = util_hton32(time); + memcpy(packet->data, &time, sizeof(time)); + packet->length = sizeof(time); + + if (!csp_send(conn, packet, 0)) { + csp_buffer_free(packet); + } +} + +gs_error_t gs_csp_uptime_service_handler(csp_conn_t * conn) +{ + return call_csp_packet_handler(conn, uptime); +} diff --git a/gomspace/libgscsp/src/transaction.c b/gomspace/libgscsp/src/transaction.c new file mode 100644 index 00000000..96ba262a --- /dev/null +++ b/gomspace/libgscsp/src/transaction.c @@ -0,0 +1,67 @@ +/* Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. */ + +#include +#include + +gs_error_t gs_csp_transaction_persistent(csp_conn_t * conn, uint32_t timeout, const void * tx_buf, + size_t tx_len, void * rx_buf, size_t rx_max_len, size_t * rx_len) +{ + GS_CHECK_HANDLE(conn != NULL); + + size_t size = (rx_max_len > tx_len) ? rx_max_len : tx_len; + csp_packet_t * packet = csp_buffer_get(size); + if (packet == NULL) { + return GS_ERROR_ALLOC; + } + + /* Copy the request */ + if (tx_len > 0 && tx_buf != NULL) { + memcpy(packet->data, tx_buf, tx_len); + } + + packet->length = tx_len; + + if (!csp_send(conn, packet, timeout)) { + csp_buffer_free(packet); + return GS_ERROR_IO; + } + + /* If no reply is expected, return now */ + if (rx_max_len == 0) { + return GS_OK; + } + + packet = csp_read(conn, timeout); + if (packet == NULL) { + return GS_ERROR_IO; + } + + gs_error_t return_val; + if (rx_max_len >= packet->length) { + size = packet->length; + return_val = GS_OK; + } else { + csp_log_error("Reply length %u, buffer only %u", packet->length, rx_max_len); + size = rx_max_len; + return_val = GS_ERROR_OVERFLOW; + } + memcpy(rx_buf, packet->data, size); + *rx_len = packet->length; + csp_buffer_free(packet); + return return_val; +} + +gs_error_t gs_csp_transaction2(uint8_t prio, uint8_t dest, uint8_t port, uint32_t timeout, const void * tx_buf, + size_t tx_len, void * rx_buf, size_t rx_max_len, size_t * rx_len, uint32_t opts) +{ + csp_conn_t * conn = csp_connect(prio, dest, port, 0, opts); + if (conn == NULL) { + return GS_ERROR_HANDLE; + } + + gs_error_t res = gs_csp_transaction_persistent(conn, timeout, tx_buf, tx_len, rx_buf, rx_max_len, rx_len); + + csp_close(conn); + + return res; +} diff --git a/gomspace/libgscsp/wscript b/gomspace/libgscsp/wscript new file mode 100644 index 00000000..a78d7f56 --- /dev/null +++ b/gomspace/libgscsp/wscript @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Copyright (c) 2013-2017 GomSpace A/S. All rights reserved. + +import gs_gcc +import gs_doc +from waflib.Build import BuildContext + +APPNAME = 'gscsp' + + +def libcsp_with_os(ctx): + if ctx.gs_is_linux(): + return 'posix' + if ctx.gs_is_freertos(): + return 'freertos' + return None + + +def libcsp_with_driver_usart(ctx): + if ctx.gs_is_linux(): + return 'linux' + return None + + +def options(ctx): + ctx.load('gs_gcc gs_doc') + gs_gcc.gs_recurse(ctx) + + +def configure(ctx): + ctx.load('gs_gcc gs_doc') + + ctx.env.append_unique('FILES_GSCSP', 'src/*.c') + ctx.env.append_unique('USE_GSCSP', ['csp', 'csp_h', 'util']) + + if ctx.options.enable_if_i2c: + ctx.env.append_unique('FILES_GSCSP', 'src/drivers/i2c/*.c') + + if ctx.gs_is_freertos(): + ctx.env.append_unique('FILES_GSCSP', 'src/drivers/can/*.c') + ctx.env.append_unique('FILES_GSCSP', 'src/drivers/kiss/*.c') + ctx.env.append_unique('FILES_GSCSP', 'src/freertos/*.c') + ctx.env.append_unique('USE_GSCSP', ['embed']) + + if ctx.gs_is_linux(): + ctx.env.append_unique('FILES_GSCSP', 'src/linux/*.c') + + # libcsp options + ctx.options.with_os = libcsp_with_os(ctx) + ctx.options.with_driver_usart = libcsp_with_driver_usart(ctx) + bindings = True if (ctx.gs_is_linux() and not ctx.gs_is_build_disabled(['shlib', 'csp_shlib'])) else False + ctx.options.enable_bindings = bindings + ctx.options.enable_python3_bindings = bindings + ctx.options.disable_stlib = True + ctx.options.enable_crc32 = True + ctx.options.with_connection_so = ctx.options.with_connection_so | 0x0040 # always CRC32, disable CSP_O_NOCRC32 + + if ctx.options.enable_if_can and ctx.options.enable_can_socketcan: + ctx.check_cc(lib='socketcan', mandatory=True) + + ctx.gs_add_doxygen(input=['include', 'lib/libcsp/include']) + + gs_gcc.gs_recurse(ctx) + + +def build(ctx): + gs_gcc.gs_recurse(ctx) + + public_include = ctx.gs_include(name=APPNAME, + includes=['include']) + + ctx.gs_objects(source=ctx.path.ant_glob(ctx.env.FILES_GSCSP), + target=APPNAME, + use=ctx.env.USE_GSCSP + [public_include]) + + ctx.gs_shlib(source=ctx.path.ant_glob(ctx.env.FILES_GSCSP), + target=APPNAME, + gs_prefix='', # make library libgscsp + gs_use_shlib=ctx.env.USE_GSCSP + [public_include]) + + ctx.gs_python_bindings(source=ctx.path.ant_glob('src/bindings/python/pygscsp.c'), + target=APPNAME, + gs_prefix='', # make library libgscsp + gs_use_shlib=ctx.env.USE_GSCSP + [APPNAME, public_include], + package='libgscsp') + + +def doc(ctx): + gs_doc.add_task_library_doc(ctx, keyvalues={ + 'gs_prod_name': 'lib'+APPNAME, + 'gs_prod_desc': 'GomSpace CSP extension', + }) + + +class Doc(BuildContext): + cmd = fun = 'doc' + + +def gs_dist(ctx): + gs_gcc.gs_recurse(ctx) + ctx.add_default_files(source_module=True) + ctx.add_files(ctx.path.ant_glob(['lib/libcsp/**/*'])) + ctx.add_license_file("CSP", "lib/libcsp/COPYING") diff --git a/gomspace/libparam_client/include/gs/param/internal/types.h b/gomspace/libparam_client/include/gs/param/internal/types.h index 5cefc4c0..2644cc62 100644 --- a/gomspace/libparam_client/include/gs/param/internal/types.h +++ b/gomspace/libparam_client/include/gs/param/internal/types.h @@ -2,8 +2,6 @@ #define GS_PARAM_INTERNAL_TYPES_H /* Copyright (c) 2013-2018 GomSpace A/S. All rights reserved. */ -#if (GS_PARAM_INTERNAL_USE) - #include #ifdef __cplusplus @@ -126,4 +124,3 @@ struct gs_param_table_instance { } #endif #endif -#endif diff --git a/gomspace/libutil/include/gs/util/log/log.h b/gomspace/libutil/include/gs/util/log/log.h index 53470a75..9781bb09 100644 --- a/gomspace/libutil/include/gs/util/log/log.h +++ b/gomspace/libutil/include/gs/util/log/log.h @@ -610,7 +610,7 @@ gs_error_t gs_log_string_to_mask(const char *str, uint8_t current_mask, uint8_t Unless levels are individually defined, this will be the default value. */ #if !defined(GS_LOG_DISABLE_ALL) -#define GS_LOG_DISABLE_ALL 0 +#define GS_LOG_DISABLE_ALL 1 #endif /** diff --git a/test/testtasks/P60DockTestTask.cpp b/test/testtasks/P60DockTestTask.cpp index 8e929e75..2a8444e8 100644 --- a/test/testtasks/P60DockTestTask.cpp +++ b/test/testtasks/P60DockTestTask.cpp @@ -6,9 +6,9 @@ */ #include +#include "P60DockTestTask.h" #include -#include "P60DockTestTask.h" P60DockTestTask::P60DockTestTask(object_id_t objectId_): SystemObject(objectId_){ @@ -23,6 +23,10 @@ ReturnValue_t P60DockTestTask::performOperation(uint8_t operationCode) { if(sendPacket() != HasReturnvaluesIF::RETURN_OK){ return HasReturnvaluesIF::RETURN_FAILED; } + + if(getParameters() != HasReturnvaluesIF::RETURN_OK){ + return HasReturnvaluesIF::RETURN_FAILED; + } return HasReturnvaluesIF::RETURN_OK; } @@ -74,17 +78,23 @@ ReturnValue_t P60DockTestTask::sendPacket(void){ ReturnValue_t P60DockTestTask::getParameters(void) { - gs_param_table_instance_t node_hk; // int result = rparam_get_full_table(&node_hk, p60dock_node, P60_PORT_RPARAM, - uint32_t timeout; - int result = p60dock_get_hk(&node_hk, p60dock_node, timeout); + uint32_t timeout = 1000; + node_hk.rows = (gs_param_table_row_t*)p60dock_hk; + node_hk.id = P60DOCK_HK; + node_hk.row_count = p60dock_hk_count; + node_hk.memory_size = P60DOCK_HK_SIZE; + node_hk.memory = hk_mem; + int result = gs_rparam_get_full_table(&node_hk, p60dockAddress, node_hk.id, + GS_RPARAM_MAGIC_CHECKSUM, timeout); + if (result != 0) { sif::info << "Error retrieving P60 Dock housekeeping\n" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } else { uint8_t tableOffsetTemperature = 0x44; - uint8_t temperature[2]; - size_t parameterSize = 2; + int16_t temperature[2]; + size_t parameterSize = sizeof(temperature); uint32_t flags = 0; result = gs_param_get_data((gs_param_table_instance_t*) &node_hk, tableOffsetTemperature, temperature, parameterSize, flags); diff --git a/test/testtasks/P60DockTestTask.h b/test/testtasks/P60DockTestTask.h index 90d810bd..e5270263 100644 --- a/test/testtasks/P60DockTestTask.h +++ b/test/testtasks/P60DockTestTask.h @@ -11,12 +11,11 @@ #include #include #include -#include -extern "C" { +#include +#include #include #include -} class P60DockTestTask: public SystemObject, @@ -40,6 +39,7 @@ private: uint8_t hk_mem[P60DOCK_HK_SIZE]; uint8_t p60dock_node = 4; + gs_param_table_instance_t node_hk; ReturnValue_t sendPacket(void); ReturnValue_t initializeCSPStack(void);