mbed OS5

Fork of UIPEthernet by Zoltan Hudak

Committer:
pilotak
Date:
Sun Aug 06 16:01:26 2017 +0000
Revision:
9:e55652bed36c
Parent:
8:4acb22344932
mBed OS5

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hudakz 4:d774541a34da 1 /*
hudakz 8:4acb22344932 2 UIPUdp.cpp - Arduino implementation of a UIP wrapper class.
hudakz 4:d774541a34da 3 Copyright (c) 2013 Norbert Truchsess <norbert.truchsess@t-online.de>
hudakz 4:d774541a34da 4 All rights reserved.
hudakz 4:d774541a34da 5
hudakz 4:d774541a34da 6 This program is free software: you can redistribute it and/or modify
hudakz 4:d774541a34da 7 it under the terms of the GNU General Public License as published by
hudakz 4:d774541a34da 8 the Free Software Foundation, either version 3 of the License, or
hudakz 4:d774541a34da 9 (at your option) any later version.
hudakz 4:d774541a34da 10
hudakz 4:d774541a34da 11 This program is distributed in the hope that it will be useful,
hudakz 4:d774541a34da 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
hudakz 4:d774541a34da 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
hudakz 4:d774541a34da 14 GNU General Public License for more details.
hudakz 4:d774541a34da 15
hudakz 4:d774541a34da 16 You should have received a copy of the GNU General Public License
hudakz 4:d774541a34da 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
hudakz 4:d774541a34da 18 */
hudakz 4:d774541a34da 19 #include "UIPEthernet.h"
hudakz 4:d774541a34da 20 #include "UIPUdp.h"
hudakz 4:d774541a34da 21 #include "Dns.h"
hudakz 4:d774541a34da 22
hudakz 4:d774541a34da 23 extern "C"
hudakz 4:d774541a34da 24 {
hudakz 4:d774541a34da 25 #include "utility/uip-conf.h"
hudakz 4:d774541a34da 26 #include "utility/uip.h"
hudakz 4:d774541a34da 27 #include "utility/uip_arp.h"
hudakz 4:d774541a34da 28 }
hudakz 4:d774541a34da 29 #if UIP_UDP
hudakz 8:4acb22344932 30 #define UIP_ARPHDRSIZE 42
hudakz 8:4acb22344932 31 #define UDPBUF ((struct uip_udpip_hdr*) &uip_buf[UIP_LLH_LEN])
hudakz 4:d774541a34da 32
hudakz 4:d774541a34da 33 // Constructor
hudakz 4:d774541a34da 34 UIPUDP::UIPUDP(void) :
hudakz 4:d774541a34da 35 _uip_udp_conn(NULL) {
hudakz 4:d774541a34da 36 memset(&appdata, 0, sizeof(appdata));
hudakz 4:d774541a34da 37 }
hudakz 4:d774541a34da 38
hudakz 4:d774541a34da 39 // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
hudakz 4:d774541a34da 40 uint8_t UIPUDP::begin(uint16_t port) {
hudakz 8:4acb22344932 41 if (!_uip_udp_conn) {
hudakz 4:d774541a34da 42 _uip_udp_conn = uip_udp_new(NULL, 0);
hudakz 4:d774541a34da 43 }
hudakz 4:d774541a34da 44
hudakz 8:4acb22344932 45 if (_uip_udp_conn) {
hudakz 4:d774541a34da 46 uip_udp_bind(_uip_udp_conn, htons(port));
hudakz 4:d774541a34da 47 _uip_udp_conn->appstate = &appdata;
hudakz 4:d774541a34da 48 return 1;
hudakz 4:d774541a34da 49 }
hudakz 4:d774541a34da 50
hudakz 4:d774541a34da 51 return 0;
hudakz 4:d774541a34da 52 }
hudakz 4:d774541a34da 53
hudakz 4:d774541a34da 54 // Finish with the UDP socket
hudakz 4:d774541a34da 55 void UIPUDP::stop(void) {
hudakz 8:4acb22344932 56 if (_uip_udp_conn) {
hudakz 4:d774541a34da 57 uip_udp_remove(_uip_udp_conn);
hudakz 4:d774541a34da 58 _uip_udp_conn->appstate = NULL;
hudakz 4:d774541a34da 59 _uip_udp_conn = NULL;
hudakz 8:4acb22344932 60 uIPEthernet.network.freeBlock(appdata.packet_in);
hudakz 8:4acb22344932 61 uIPEthernet.network.freeBlock(appdata.packet_next);
hudakz 8:4acb22344932 62 uIPEthernet.network.freeBlock(appdata.packet_out);
hudakz 4:d774541a34da 63 memset(&appdata, 0, sizeof(appdata));
hudakz 4:d774541a34da 64 }
hudakz 4:d774541a34da 65 }
hudakz 4:d774541a34da 66
hudakz 4:d774541a34da 67 // Sending UDP packets
hudakz 4:d774541a34da 68 // Start building up a packet to send to the remote host specific in ip and port
hudakz 4:d774541a34da 69
hudakz 4:d774541a34da 70 // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
hudakz 4:d774541a34da 71 int UIPUDP::beginPacket(IPAddress ip, uint16_t port) {
hudakz 8:4acb22344932 72 uIPEthernet.tick();
hudakz 8:4acb22344932 73 if (ip && port) {
hudakz 4:d774541a34da 74 uip_ipaddr_t ripaddr;
hudakz 4:d774541a34da 75 uip_ip_addr(&ripaddr, ip);
hudakz 8:4acb22344932 76 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 77 printf("udp beginPacket, ");
hudakz 8:4acb22344932 78 #endif
hudakz 8:4acb22344932 79 if (_uip_udp_conn) {
hudakz 4:d774541a34da 80 _uip_udp_conn->rport = htons(port);
hudakz 4:d774541a34da 81 uip_ipaddr_copy(_uip_udp_conn->ripaddr, &ripaddr);
hudakz 4:d774541a34da 82 }
hudakz 4:d774541a34da 83 else {
hudakz 4:d774541a34da 84 _uip_udp_conn = uip_udp_new(&ripaddr, htons(port));
hudakz 8:4acb22344932 85 if (_uip_udp_conn)
hudakz 4:d774541a34da 86 {
hudakz 8:4acb22344932 87 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 88 printf("new connection, ");
hudakz 8:4acb22344932 89 #endif
hudakz 4:d774541a34da 90 _uip_udp_conn->appstate = &appdata;
hudakz 4:d774541a34da 91 }
hudakz 4:d774541a34da 92 else
hudakz 4:d774541a34da 93 {
hudakz 8:4acb22344932 94 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 95 printf("failed to allocate new connection\r\n");
hudakz 8:4acb22344932 96 #endif
hudakz 4:d774541a34da 97 return 0;
hudakz 4:d774541a34da 98 }
hudakz 4:d774541a34da 99 }
hudakz 4:d774541a34da 100
hudakz 8:4acb22344932 101 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 102 printf("rip: %s, port: %d\r\n", ip.asString(), port);
hudakz 8:4acb22344932 103 #endif
hudakz 4:d774541a34da 104 }
hudakz 4:d774541a34da 105
hudakz 8:4acb22344932 106 if (_uip_udp_conn) {
hudakz 8:4acb22344932 107 if (appdata.packet_out == NOBLOCK) {
hudakz 8:4acb22344932 108 appdata.packet_out = uIPEthernet.network.allocBlock(UIP_UDP_MAXPACKETSIZE);
hudakz 4:d774541a34da 109 appdata.out_pos = UIP_UDP_PHYH_LEN;
hudakz 8:4acb22344932 110 if (appdata.packet_out != NOBLOCK)
hudakz 4:d774541a34da 111 return 1;
hudakz 8:4acb22344932 112 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 4:d774541a34da 113 else
hudakz 8:4acb22344932 114 printf("failed to allocate memory for packet\r\n");
hudakz 8:4acb22344932 115 #endif
hudakz 4:d774541a34da 116 }
hudakz 4:d774541a34da 117
hudakz 8:4acb22344932 118 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 4:d774541a34da 119 else
hudakz 8:4acb22344932 120 printf("previous packet on that connection not sent yet\r\n");
hudakz 8:4acb22344932 121 #endif
hudakz 4:d774541a34da 122 }
hudakz 4:d774541a34da 123
hudakz 4:d774541a34da 124 return 0;
hudakz 4:d774541a34da 125 }
hudakz 4:d774541a34da 126
hudakz 4:d774541a34da 127 // Start building up a packet to send to the remote host specific in host and port
hudakz 4:d774541a34da 128
hudakz 4:d774541a34da 129 // Returns 1 if successful, 0 if there was a problem resolving the hostname or port
hudakz 4:d774541a34da 130 int UIPUDP::beginPacket(const char* host, uint16_t port) {
hudakz 4:d774541a34da 131
hudakz 4:d774541a34da 132 // Look up the host first
hudakz 4:d774541a34da 133 int ret = 0;
hudakz 4:d774541a34da 134 DNSClient dns;
hudakz 4:d774541a34da 135 IPAddress remote_addr;
hudakz 4:d774541a34da 136
hudakz 8:4acb22344932 137 dns.begin(uIPEthernet.dnsServerIP());
hudakz 4:d774541a34da 138 ret = dns.getHostByName(host, remote_addr);
hudakz 8:4acb22344932 139 if (ret == 1) {
hudakz 4:d774541a34da 140 return beginPacket(remote_addr, port);
hudakz 4:d774541a34da 141 }
hudakz 4:d774541a34da 142 else {
hudakz 4:d774541a34da 143 return ret;
hudakz 4:d774541a34da 144 }
hudakz 4:d774541a34da 145 }
hudakz 4:d774541a34da 146
hudakz 4:d774541a34da 147 // Finish off this packet and send it
hudakz 4:d774541a34da 148
hudakz 4:d774541a34da 149 // Returns 1 if the packet was sent successfully, 0 if there was an error
hudakz 4:d774541a34da 150 int UIPUDP::endPacket(void) {
hudakz 8:4acb22344932 151 if (_uip_udp_conn && appdata.packet_out != NOBLOCK) {
hudakz 4:d774541a34da 152 appdata.send = true;
hudakz 8:4acb22344932 153 uIPEthernet.network.resizeBlock(appdata.packet_out, 0, appdata.out_pos);
hudakz 4:d774541a34da 154 uip_udp_periodic_conn(_uip_udp_conn);
hudakz 8:4acb22344932 155 if (uip_len > 0) {
hudakz 4:d774541a34da 156 _send(&appdata);
hudakz 4:d774541a34da 157 return 1;
hudakz 4:d774541a34da 158 }
hudakz 4:d774541a34da 159 }
hudakz 4:d774541a34da 160
hudakz 4:d774541a34da 161 return 0;
hudakz 4:d774541a34da 162 }
hudakz 4:d774541a34da 163
hudakz 4:d774541a34da 164 // Write a single byte into the packet
hudakz 4:d774541a34da 165 size_t UIPUDP::write(uint8_t c) {
hudakz 4:d774541a34da 166 return write(&c, 1);
hudakz 4:d774541a34da 167 }
hudakz 4:d774541a34da 168
hudakz 4:d774541a34da 169 // Write size bytes from buffer into the packet
hudakz 4:d774541a34da 170 size_t UIPUDP::write(const uint8_t* buffer, size_t size) {
hudakz 8:4acb22344932 171 if (appdata.packet_out != NOBLOCK) {
hudakz 8:4acb22344932 172 size_t ret = uIPEthernet.network.writePacket(appdata.packet_out, appdata.out_pos, (uint8_t*)buffer, size);
hudakz 4:d774541a34da 173 appdata.out_pos += ret;
hudakz 4:d774541a34da 174 return ret;
hudakz 4:d774541a34da 175 }
hudakz 4:d774541a34da 176
hudakz 4:d774541a34da 177 return 0;
hudakz 4:d774541a34da 178 }
hudakz 4:d774541a34da 179
hudakz 4:d774541a34da 180 // Start processing the next available incoming packet
hudakz 4:d774541a34da 181
hudakz 4:d774541a34da 182 // Returns the size of the packet in bytes, or 0 if no packets are available
hudakz 4:d774541a34da 183 int UIPUDP::parsePacket(void) {
hudakz 8:4acb22344932 184 uIPEthernet.tick();
hudakz 8:4acb22344932 185 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 186 if (appdata.packet_in != NOBLOCK) {
hudakz 8:4acb22344932 187 printf("udp parsePacket freeing previous packet: %d\r\n", appdata.packet_in);
hudakz 4:d774541a34da 188 }
hudakz 8:4acb22344932 189 #endif
hudakz 8:4acb22344932 190 uIPEthernet.network.freeBlock(appdata.packet_in);
hudakz 4:d774541a34da 191
hudakz 4:d774541a34da 192 appdata.packet_in = appdata.packet_next;
hudakz 4:d774541a34da 193 appdata.packet_next = NOBLOCK;
hudakz 4:d774541a34da 194
hudakz 8:4acb22344932 195 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 196 if (appdata.packet_in != NOBLOCK) {
hudakz 8:4acb22344932 197 printf("udp parsePacket received packet: %d", appdata.packet_in);
hudakz 4:d774541a34da 198 }
hudakz 8:4acb22344932 199 #endif
hudakz 4:d774541a34da 200
hudakz 8:4acb22344932 201 int size = uIPEthernet.network.blockSize(appdata.packet_in);
hudakz 8:4acb22344932 202 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 203 if (appdata.packet_in != NOBLOCK) {
hudakz 8:4acb22344932 204 printf(", size: %d\r\n", size);
hudakz 4:d774541a34da 205 }
hudakz 8:4acb22344932 206 #endif
hudakz 4:d774541a34da 207 return size;
hudakz 4:d774541a34da 208 }
hudakz 4:d774541a34da 209
hudakz 4:d774541a34da 210 // Number of bytes remaining in the current packet
hudakz 4:d774541a34da 211 int UIPUDP::available(void) {
hudakz 8:4acb22344932 212 uIPEthernet.tick();
hudakz 8:4acb22344932 213 return uIPEthernet.network.blockSize(appdata.packet_in);
hudakz 4:d774541a34da 214 }
hudakz 4:d774541a34da 215
hudakz 4:d774541a34da 216 // Read a single byte from the current packet
hudakz 4:d774541a34da 217 int UIPUDP::read(void) {
hudakz 8:4acb22344932 218 static unsigned char c;
hudakz 8:4acb22344932 219 if (read(&c, 1) > 0) {
hudakz 4:d774541a34da 220 return c;
hudakz 4:d774541a34da 221 }
hudakz 4:d774541a34da 222
hudakz 4:d774541a34da 223 return -1;
hudakz 4:d774541a34da 224 }
hudakz 4:d774541a34da 225
hudakz 4:d774541a34da 226 // Read up to len bytes from the current packet and place them into buffer
hudakz 4:d774541a34da 227
hudakz 4:d774541a34da 228 // Returns the number of bytes read, or 0 if none are available
hudakz 4:d774541a34da 229 int UIPUDP::read(unsigned char* buffer, size_t len) {
hudakz 8:4acb22344932 230 uIPEthernet.tick();
hudakz 8:4acb22344932 231 if (appdata.packet_in != NOBLOCK) {
hudakz 8:4acb22344932 232 memaddress read = uIPEthernet.network.readPacket(appdata.packet_in, 0, buffer, len);
hudakz 8:4acb22344932 233 if (read == uIPEthernet.network.blockSize(appdata.packet_in)) {
hudakz 8:4acb22344932 234 uIPEthernet.network.freeBlock(appdata.packet_in);
hudakz 4:d774541a34da 235 appdata.packet_in = NOBLOCK;
hudakz 4:d774541a34da 236 }
hudakz 4:d774541a34da 237 else
hudakz 8:4acb22344932 238 uIPEthernet.network.resizeBlock(appdata.packet_in, read);
hudakz 4:d774541a34da 239 return read;
hudakz 4:d774541a34da 240 }
hudakz 4:d774541a34da 241
hudakz 4:d774541a34da 242 return 0;
hudakz 4:d774541a34da 243 }
hudakz 4:d774541a34da 244
hudakz 4:d774541a34da 245 // Return the next byte from the current packet without moving on to the next byte
hudakz 4:d774541a34da 246 int UIPUDP::peek(void) {
hudakz 8:4acb22344932 247 uIPEthernet.tick();
hudakz 8:4acb22344932 248 if (appdata.packet_in != NOBLOCK) {
hudakz 4:d774541a34da 249 unsigned char c;
hudakz 8:4acb22344932 250 if (uIPEthernet.network.readPacket(appdata.packet_in, 0, &c, 1) == 1)
hudakz 4:d774541a34da 251 return c;
hudakz 4:d774541a34da 252 }
hudakz 4:d774541a34da 253
hudakz 4:d774541a34da 254 return -1;
hudakz 4:d774541a34da 255 }
hudakz 4:d774541a34da 256
hudakz 4:d774541a34da 257 // Finish reading the current packet
hudakz 4:d774541a34da 258 void UIPUDP::flush(void) {
hudakz 8:4acb22344932 259 uIPEthernet.tick();
hudakz 8:4acb22344932 260 uIPEthernet.network.freeBlock(appdata.packet_in);
hudakz 4:d774541a34da 261 appdata.packet_in = NOBLOCK;
hudakz 4:d774541a34da 262 }
hudakz 4:d774541a34da 263
hudakz 4:d774541a34da 264 // Return the IP address of the host who sent the current incoming packet
hudakz 4:d774541a34da 265 IPAddress UIPUDP::remoteIP(void) {
hudakz 4:d774541a34da 266 return _uip_udp_conn ? ip_addr_uip(_uip_udp_conn->ripaddr) : IPAddress();
hudakz 4:d774541a34da 267 }
hudakz 4:d774541a34da 268
hudakz 4:d774541a34da 269 // Return the port of the host who sent the current incoming packet
hudakz 4:d774541a34da 270 uint16_t UIPUDP::remotePort(void) {
hudakz 4:d774541a34da 271 return _uip_udp_conn ? ntohs(_uip_udp_conn->rport) : 0;
hudakz 4:d774541a34da 272 }
hudakz 4:d774541a34da 273
hudakz 8:4acb22344932 274 // UIP callback function
hudakz 4:d774541a34da 275 void uipudp_appcall(void) {
hudakz 8:4acb22344932 276 if (uip_udp_userdata_t * data = (uip_udp_userdata_t *) (uip_udp_conn->appstate)) {
hudakz 8:4acb22344932 277 if (uip_newdata()) {
hudakz 8:4acb22344932 278 if (data->packet_next == NOBLOCK) {
hudakz 4:d774541a34da 279 uip_udp_conn->rport = UDPBUF->srcport;
hudakz 4:d774541a34da 280 uip_ipaddr_copy(uip_udp_conn->ripaddr, UDPBUF->srcipaddr);
hudakz 8:4acb22344932 281 data->packet_next = uIPEthernet.network.allocBlock(ntohs(UDPBUF->udplen) - UIP_UDPH_LEN);
hudakz 4:d774541a34da 282
hudakz 4:d774541a34da 283 //if we are unable to allocate memory the packet is dropped. udp doesn't guarantee packet delivery
hudakz 8:4acb22344932 284 if (data->packet_next != NOBLOCK) {
hudakz 4:d774541a34da 285
hudakz 4:d774541a34da 286 //discard Linklevel and IP and udp-header and any trailing bytes:
hudakz 8:4acb22344932 287 uIPEthernet.network.copyPacket
hudakz 4:d774541a34da 288 (
hudakz 4:d774541a34da 289 data->packet_next,
hudakz 4:d774541a34da 290 0,
hudakz 8:4acb22344932 291 UIPEthernet::in_packet,
hudakz 4:d774541a34da 292 UIP_UDP_PHYH_LEN,
hudakz 8:4acb22344932 293 uIPEthernet.network.blockSize(data->packet_next)
hudakz 4:d774541a34da 294 );
hudakz 8:4acb22344932 295 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 296 printf
hudakz 8:4acb22344932 297 (
hudakz 8:4acb22344932 298 "udp, uip_newdata received packet: %d, size: %d\r\n",
hudakz 8:4acb22344932 299 data->packet_next,
hudakz 8:4acb22344932 300 UIPEthernet.network.blockSize(data->packet_next)
hudakz 8:4acb22344932 301 );
hudakz 8:4acb22344932 302 #endif
hudakz 4:d774541a34da 303 }
hudakz 4:d774541a34da 304 }
hudakz 4:d774541a34da 305 }
hudakz 4:d774541a34da 306
hudakz 8:4acb22344932 307 if (uip_poll() && data->send)
hudakz 4:d774541a34da 308 {
hudakz 4:d774541a34da 309 //set uip_slen (uip private) by calling uip_udp_send
hudakz 8:4acb22344932 310 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 311 printf
hudakz 8:4acb22344932 312 (
hudakz 8:4acb22344932 313 "udp, uip_poll preparing packet to send: %d, size: %d\r\n",
hudakz 8:4acb22344932 314 data->packet_out,
hudakz 8:4acb22344932 315 UIPEthernet.network.blockSize(data->packet_out)
hudakz 8:4acb22344932 316 );
hudakz 8:4acb22344932 317 #endif
hudakz 8:4acb22344932 318 UIPEthernet::uip_packet = data->packet_out;
hudakz 8:4acb22344932 319 UIPEthernet::uip_hdrlen = UIP_UDP_PHYH_LEN;
hudakz 4:d774541a34da 320 uip_udp_send(data->out_pos - (UIP_UDP_PHYH_LEN));
hudakz 4:d774541a34da 321 }
hudakz 4:d774541a34da 322 }
hudakz 4:d774541a34da 323 }
hudakz 4:d774541a34da 324
hudakz 4:d774541a34da 325 /**
hudakz 4:d774541a34da 326 * @brief
hudakz 4:d774541a34da 327 * @note
hudakz 4:d774541a34da 328 * @param
hudakz 4:d774541a34da 329 * @retval
hudakz 4:d774541a34da 330 */
hudakz 4:d774541a34da 331 void UIPUDP::_send(uip_udp_userdata_t* data) {
hudakz 4:d774541a34da 332 uip_arp_out(); //add arp
hudakz 8:4acb22344932 333 if (uip_len == UIP_ARPHDRSIZE) {
hudakz 8:4acb22344932 334 UIPEthernet::uip_packet = NOBLOCK;
hudakz 8:4acb22344932 335 UIPEthernet::packetstate &= ~UIPETHERNET_SENDPACKET;
hudakz 8:4acb22344932 336 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 337 printf("udp, uip_poll results in ARP-packet\r\n");
hudakz 8:4acb22344932 338 #endif
hudakz 4:d774541a34da 339 }
hudakz 4:d774541a34da 340 else {
hudakz 4:d774541a34da 341
hudakz 8:4acb22344932 342 //arp found ethaddr for ip (otherwise packet is replaced by arp-request)
hudakz 4:d774541a34da 343 data->send = false;
hudakz 4:d774541a34da 344 data->packet_out = NOBLOCK;
hudakz 8:4acb22344932 345 UIPEthernet::packetstate |= UIPETHERNET_SENDPACKET;
hudakz 8:4acb22344932 346 #ifdef UIPETHERNET_DEBUG_UDP
hudakz 8:4acb22344932 347 printf("udp, uip_packet to send: %d\r\n", UIPEthernet::uip_packet);
hudakz 8:4acb22344932 348 #endif
hudakz 4:d774541a34da 349 }
hudakz 4:d774541a34da 350
hudakz 8:4acb22344932 351 uIPEthernet.network_send();
hudakz 4:d774541a34da 352 }
hudakz 4:d774541a34da 353 #endif
hudakz 8:4acb22344932 354