[6319] | 1 | /** @file wlan_exp_transport.c |
---|
| 2 | * @brief Experiment Framework (Transport) |
---|
| 3 | * |
---|
| 4 | * Handles UDP transmissions and reception as well as process commands for the |
---|
| 5 | * transport group. |
---|
| 6 | * |
---|
| 7 | * @copyright Copyright 2013-2019, Mango Communications. All rights reserved. |
---|
| 8 | * Distributed under the Mango Communications Reference Design License |
---|
| 9 | * See LICENSE.txt included in the design archive or |
---|
| 10 | * at http://mangocomm.com/802.11/license |
---|
| 11 | * |
---|
| 12 | * This file is part of the Mango 802.11 Reference Design (https://mangocomm.com/802.11) |
---|
| 13 | */ |
---|
| 14 | |
---|
| 15 | /***************************** Include Files *********************************/ |
---|
| 16 | |
---|
| 17 | #include "wlan_mac_high_sw_config.h" |
---|
| 18 | |
---|
| 19 | #if WLAN_SW_CONFIG_ENABLE_WLAN_EXP |
---|
| 20 | |
---|
| 21 | #include <xparameters.h> |
---|
| 22 | #include <xil_io.h> |
---|
| 23 | #include <xstatus.h> |
---|
| 24 | #include <stdlib.h> |
---|
| 25 | #include <stdio.h> |
---|
| 26 | #include <string.h> |
---|
| 27 | |
---|
| 28 | #include "wlan_mac_common.h" |
---|
| 29 | #include "wlan_mac_high.h" |
---|
| 30 | #include "wlan_platform_high.h" |
---|
| 31 | #include "wlan_platform_common.h" |
---|
| 32 | #include "wlan_mac_dl_list.h" |
---|
| 33 | #include "wlan_mac_queue.h" |
---|
| 34 | #include "wlan_mac_network_info.h" |
---|
| 35 | #include "wlan_mac_station_info.h" |
---|
| 36 | |
---|
| 37 | #include "wlan_exp_common.h" |
---|
| 38 | #include "wlan_exp_node.h" |
---|
| 39 | #include "wlan_exp_transport.h" |
---|
| 40 | #include "wlan_exp_ip_udp_socket.h" |
---|
| 41 | |
---|
| 42 | // Internal structure to this file. It never is sent to a host OTW |
---|
| 43 | typedef struct _wlan_exp_transport_info_t{ |
---|
| 44 | u32 group_id; |
---|
| 45 | u32 unicast_port; |
---|
| 46 | u32 broadcast_port; |
---|
| 47 | u32 max_pkt_words; |
---|
| 48 | int socket_unicast; |
---|
| 49 | int socket_broadcast; |
---|
| 50 | } _wlan_exp_transport_info_t; |
---|
| 51 | |
---|
| 52 | // Transport information |
---|
| 53 | static _wlan_exp_transport_info_t wlan_exp_transport_info; |
---|
| 54 | |
---|
| 55 | // Defined in wlan_exp_node.c |
---|
| 56 | extern platform_high_dev_info_t platform_high_dev_info; |
---|
| 57 | extern wlan_exp_node_info_t wlan_exp_node_info; |
---|
| 58 | |
---|
| 59 | // Defined in wlan_mac_high.c |
---|
| 60 | extern wlan_mac_hw_info_t wlan_mac_hw_info; |
---|
| 61 | |
---|
| 62 | // Queues for wlan_exp Rx/Tx |
---|
| 63 | static int _wlan_exp_eth_rx_qid; |
---|
| 64 | static int _wlan_exp_eth_tx_qid; |
---|
| 65 | |
---|
| 66 | static sockaddr_in_t* gl_tx_to; |
---|
| 67 | |
---|
| 68 | void _wlan_exp_tx_queue_occupancy_change(int callback_arg, u32 queue_len); |
---|
| 69 | int _transport_config_socket(int* socket_index, u32 udp_port); |
---|
| 70 | |
---|
| 71 | /*****************************************************************************/ |
---|
| 72 | /** |
---|
| 73 | * @brief Transport subsystem initialization |
---|
| 74 | * |
---|
| 75 | * @return int - WLAN_SUCCESS or WLAN_FAILURE |
---|
| 76 | ******************************************************************************/ |
---|
| 77 | int transport_init() { |
---|
| 78 | u8 ip_addr[IP_ADDR_LEN]; |
---|
| 79 | |
---|
| 80 | int status = WLAN_SUCCESS; |
---|
| 81 | |
---|
| 82 | // Open a queue for Rx Ethernet frames |
---|
| 83 | _wlan_exp_eth_rx_qid = queue_open((void*)wlan_null_callback, 0, WLAN_EXP_MAX_QUEUE_LEN); |
---|
| 84 | |
---|
| 85 | // Open a queue for Tx Ethernet frames |
---|
| 86 | _wlan_exp_eth_tx_qid = queue_open((void*)_wlan_exp_tx_queue_occupancy_change, 0, WLAN_EXP_MAX_QUEUE_LEN); |
---|
| 87 | |
---|
| 88 | if((_wlan_exp_eth_rx_qid == -1) || (_wlan_exp_eth_tx_qid == -1)) xil_printf("Error in wlan_eth_util_init(), unable to open queue\n"); |
---|
| 89 | |
---|
| 90 | ip_addr[0] = (WLAN_EXP_DEFAULT_IP_ADDR >> 24) & 0xFF; |
---|
| 91 | ip_addr[1] = (WLAN_EXP_DEFAULT_IP_ADDR >> 16) & 0xFF; |
---|
| 92 | ip_addr[2] = (WLAN_EXP_DEFAULT_IP_ADDR >> 8) & 0xFF; |
---|
| 93 | ip_addr[3] = (WLAN_EXP_DEFAULT_IP_ADDR ) & 0xFF; // IP ADDR = w.x.y.z |
---|
| 94 | |
---|
| 95 | // Initialize the wlan_exp IP/UDP transport |
---|
| 96 | ip_udp_init(wlan_mac_hw_info.hw_addr_wlan_exp, ip_addr); |
---|
| 97 | |
---|
| 98 | // Print MAC address and IP address |
---|
| 99 | xil_printf(" wlan_exp MAC Address: %02x:%02x:%02x:%02x:%02x:%02x\n", |
---|
| 100 | wlan_mac_hw_info.hw_addr_wlan_exp[0], wlan_mac_hw_info.hw_addr_wlan_exp[1], wlan_mac_hw_info.hw_addr_wlan_exp[2], |
---|
| 101 | wlan_mac_hw_info.hw_addr_wlan_exp[3], wlan_mac_hw_info.hw_addr_wlan_exp[4], wlan_mac_hw_info.hw_addr_wlan_exp[5]); |
---|
| 102 | xil_printf(" wlan_exp IP Address: %d.%d.%d.%d\n", |
---|
| 103 | ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]); |
---|
| 104 | |
---|
| 105 | |
---|
| 106 | // Initialize transport info structure |
---|
| 107 | wlan_exp_transport_info.max_pkt_words = WLAN_EXP_DEFAULT_MAX_PACKET_WORDS; |
---|
| 108 | wlan_exp_transport_info.socket_unicast = SOCKET_INVALID_SOCKET; |
---|
| 109 | wlan_exp_transport_info.socket_broadcast = SOCKET_INVALID_SOCKET; |
---|
| 110 | |
---|
| 111 | wlan_exp_transport_info.group_id = 0; |
---|
| 112 | transport_set_ip_addr(ip_addr); |
---|
| 113 | |
---|
| 114 | // Configure the Sockets for each Ethernet Interface |
---|
| 115 | status = transport_config_sockets(WLAN_EXP_DEFAULT_UDP_UNICAST_PORT, WLAN_EXP_DEFAULT_UDP_MULTICAST_PORT); |
---|
| 116 | |
---|
| 117 | if (status != WLAN_SUCCESS) { |
---|
| 118 | wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Cannot configure sockets for Ethernet\n"); |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | gl_tx_to = NULL; |
---|
| 122 | |
---|
| 123 | return status; |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | /*****************************************************************************/ |
---|
| 127 | /** |
---|
| 128 | * @brief Append a UDP reception for future processing |
---|
| 129 | * |
---|
| 130 | * This function accepts an Ethernet queue buffer and enqueues it for |
---|
| 131 | * later processing by wlan_exp. The framework uses this structure to defer |
---|
| 132 | * processing to outside of an interrupt context in platforms whose wlan_exp |
---|
| 133 | * Ethernet peripheral is interrupt-based. |
---|
| 134 | * |
---|
| 135 | * @return int - WLAN_SUCCESS or WLAN_FAILURE |
---|
| 136 | ******************************************************************************/ |
---|
| 137 | int wlan_exp_append_rx(dl_entry* queue_entry){ |
---|
| 138 | int status; |
---|
| 139 | |
---|
| 140 | status = queue_enqueue(_wlan_exp_eth_rx_qid, queue_entry); |
---|
| 141 | if( status == WLAN_FAILURE ){ |
---|
| 142 | queue_checkin(queue_entry); |
---|
| 143 | } |
---|
| 144 | |
---|
| 145 | return status; |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | /*****************************************************************************/ |
---|
| 149 | /** |
---|
| 150 | * @brief Append a UDP transmission for future processing |
---|
| 151 | * |
---|
| 152 | * This function accepts an Ethernet queue buffer and enqueues it for |
---|
| 153 | * later transmission by the platform's wlan_exp Ethernet peripheral. |
---|
| 154 | * |
---|
| 155 | * @return int - WLAN_SUCCESS or WLAN_FAILURE |
---|
| 156 | ******************************************************************************/ |
---|
| 157 | int wlan_exp_append_tx(dl_entry* queue_entry){ |
---|
| 158 | int status; |
---|
| 159 | interrupt_state_t prev_interrupt_state; |
---|
| 160 | |
---|
| 161 | prev_interrupt_state = wlan_platform_intc_stop(); |
---|
| 162 | status = queue_enqueue(_wlan_exp_eth_tx_qid, queue_entry); |
---|
| 163 | |
---|
| 164 | if( status == WLAN_FAILURE ){ |
---|
| 165 | queue_checkin(queue_entry); |
---|
| 166 | } |
---|
| 167 | wlan_platform_intc_set_state(prev_interrupt_state); |
---|
| 168 | |
---|
| 169 | return status; |
---|
| 170 | } |
---|
| 171 | |
---|
| 172 | /*****************************************************************************/ |
---|
| 173 | /** |
---|
| 174 | * @brief Get the current length of the wlan_exp Tx queue |
---|
| 175 | * |
---|
| 176 | * @return int - length of the Tx queue |
---|
| 177 | ******************************************************************************/ |
---|
| 178 | int wlan_exp_get_tx_queue_length(){ |
---|
| 179 | return queue_length(_wlan_exp_eth_tx_qid); |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | |
---|
| 183 | /*****************************************************************************/ |
---|
| 184 | /** |
---|
| 185 | * @brief Poll enqueued UDP receptions |
---|
| 186 | * |
---|
| 187 | * This function is called by outside of an interrupt context and is used to process |
---|
| 188 | * any received UDP frames enqueued by wlan_exp_append_rx(). By executing outside of |
---|
| 189 | * an ISR, critical 802.11 behaviors are able to take priority over wlan_exp |
---|
| 190 | * behaviors. |
---|
| 191 | * |
---|
| 192 | * @return None |
---|
| 193 | * |
---|
| 194 | *****************************************************************************/ |
---|
| 195 | void wlan_exp_transport_poll() { |
---|
| 196 | u32 i; |
---|
| 197 | u32 num_packets; |
---|
| 198 | wlan_exp_eth_rx_queue_buffer_t* wlan_exp_eth_rx_queue_buffer; |
---|
| 199 | dl_entry* wlan_exp_eth_rx_queue_entry; |
---|
| 200 | |
---|
| 201 | num_packets = WLAN_MIN(queue_length(_wlan_exp_eth_rx_qid), WLAN_EXP_MAX_RX_PROC_PER_POLL); |
---|
| 202 | |
---|
| 203 | for(i=0; i<num_packets; i++){ |
---|
| 204 | |
---|
| 205 | wlan_exp_eth_rx_queue_entry = queue_dequeue(_wlan_exp_eth_rx_qid); |
---|
| 206 | |
---|
| 207 | if(wlan_exp_eth_rx_queue_entry == NULL){ |
---|
| 208 | // We are done processing the enqueued list of commands |
---|
| 209 | return; |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | wlan_exp_eth_rx_queue_buffer = (wlan_exp_eth_rx_queue_buffer_t*)(wlan_exp_eth_rx_queue_entry->data); |
---|
| 213 | |
---|
| 214 | // The only way for this to fail is if there are no available queue entries in the free pool. |
---|
| 215 | // We are forced to skip processing this command if this is the case. |
---|
| 216 | |
---|
| 217 | // We will need socket details about this reception in order to form a response. Rather than pass this information |
---|
| 218 | // through a series of context switches as function arguments, we will hold on to it in a global that is only |
---|
| 219 | // non-null for the duration of wlan_exp_transport_receive(). |
---|
| 220 | gl_tx_to = &(wlan_exp_eth_rx_queue_buffer->rx_from); |
---|
| 221 | // Pass packet off to transport command processing |
---|
| 222 | wlan_exp_transport_receive(wlan_exp_eth_rx_queue_buffer); |
---|
| 223 | gl_tx_to = NULL; |
---|
| 224 | |
---|
| 225 | // Return the received packet to the free pool |
---|
| 226 | queue_checkin(wlan_exp_eth_rx_queue_entry); |
---|
| 227 | } |
---|
| 228 | } |
---|
| 229 | |
---|
| 230 | /*****************************************************************************/ |
---|
| 231 | /** |
---|
| 232 | * @brief Fill IP/UDP headers in response packet |
---|
| 233 | * |
---|
| 234 | * This function is the final step before a packet is ready to be sent. It fills |
---|
| 235 | * in the IP and UDP headers of the packet, which include a checksum that depends |
---|
| 236 | * on the total length of the frame. |
---|
| 237 | * |
---|
| 238 | * @return WLAN_SUCCESS or WLAN_FAILURE |
---|
| 239 | *****************************************************************************/ |
---|
| 240 | int wlan_exp_transport_fill_headers_response(eth_tx_queue_buffer_t* eth_tx_queue_buffer){ |
---|
| 241 | // This function will fill in the Ethernet, IP, UDP, and transport headers needed |
---|
| 242 | // to send a response packet. |
---|
| 243 | |
---|
| 244 | int length; |
---|
| 245 | u32 unicast_port; |
---|
| 246 | |
---|
| 247 | unicast_port = wlan_exp_transport_info.unicast_port; |
---|
| 248 | |
---|
| 249 | if(eth_tx_queue_buffer == NULL) return WLAN_FAILURE; |
---|
| 250 | |
---|
| 251 | // Fill in Ethernet, IP, and UDP headers using socket function |
---|
| 252 | |
---|
| 253 | length = ip_udp_template_init(eth_tx_queue_buffer->seg0, unicast_port, gl_tx_to); |
---|
| 254 | if(length == WLAN_FAILURE){ |
---|
| 255 | return WLAN_FAILURE; |
---|
| 256 | } |
---|
| 257 | |
---|
| 258 | // We do not need to update the length metadata in the packet queue buffer. We had already |
---|
| 259 | // set aside the necessary bytes of the headers in wlan_exp_transport_checkout_response() |
---|
| 260 | |
---|
| 261 | return WLAN_SUCCESS; |
---|
| 262 | } |
---|
| 263 | |
---|
| 264 | /*****************************************************************************/ |
---|
| 265 | /** |
---|
| 266 | * @brief Handle wlan_exp Tx Queue |
---|
| 267 | * |
---|
| 268 | * This function is called in an interrupt context and is responsible for |
---|
| 269 | * dequeuing packets from the wlan_exp Tx queue. |
---|
| 270 | * |
---|
| 271 | * @return u32 - the remaining length of the wlan_exp Tx queue after transmissions |
---|
| 272 | * are complete |
---|
| 273 | *****************************************************************************/ |
---|
| 274 | u32 wlan_exp_handle_tx_queue(){ |
---|
| 275 | dl_entry* packet_to_process; |
---|
| 276 | u32 i; |
---|
| 277 | int status; |
---|
| 278 | |
---|
| 279 | for(i = 0; i < WLAN_MIN(queue_length(_wlan_exp_eth_tx_qid), WLAN_MAX_WLAN_EXP_ETH_TX_PROCESS_PER_ISR); i++){ |
---|
| 280 | packet_to_process = queue_dequeue(_wlan_exp_eth_tx_qid); |
---|
| 281 | if(packet_to_process == NULL) return 0; // should not be possible |
---|
| 282 | |
---|
| 283 | // Platform Ethernet Send functions are required to check in |
---|
| 284 | // packet_to_process when they are done with them if they were successful. |
---|
| 285 | status = wlan_platform_wlan_exp_eth_send((eth_tx_queue_buffer_t*)(packet_to_process->data)); |
---|
| 286 | |
---|
| 287 | if(status != 0){ |
---|
| 288 | // We were unable to transmit this packet. This is mostly likely due to an ongoing transmission |
---|
| 289 | // causing there to be no free BDs. Re-enqueue this packet back into the head of the _eth_tx_qid |
---|
| 290 | // queue; |
---|
| 291 | status = enqueue_head(_wlan_exp_eth_tx_qid, packet_to_process); |
---|
| 292 | |
---|
| 293 | if( status == -1 ){ |
---|
| 294 | queue_checkin(packet_to_process); |
---|
| 295 | } |
---|
| 296 | } |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | return queue_length(_wlan_exp_eth_rx_qid); |
---|
| 300 | } |
---|
| 301 | |
---|
| 302 | |
---|
| 303 | |
---|
| 304 | |
---|
| 305 | /*****************************************************************************/ |
---|
| 306 | /** |
---|
| 307 | * @brief Process the received UDP packet by the transport |
---|
| 308 | * |
---|
| 309 | * @param wlan_exp_eth_rx_queue_buffer* - Received Ethernet frame |
---|
| 310 | * |
---|
| 311 | * @return None |
---|
| 312 | * |
---|
| 313 | *****************************************************************************/ |
---|
| 314 | void wlan_exp_transport_receive(wlan_exp_eth_rx_queue_buffer_t* wlan_exp_eth_rx_queue_buffer){ |
---|
| 315 | |
---|
| 316 | int resp_sent; |
---|
| 317 | u16 dest_id; |
---|
| 318 | u16 src_id; |
---|
| 319 | u16 seq_num; |
---|
| 320 | u16 flags; |
---|
| 321 | u32 node_id; |
---|
| 322 | u32 group_id; |
---|
| 323 | u16 resp_template_length = 0; |
---|
| 324 | |
---|
| 325 | eth_tx_queue_buffer_t* eth_tx_queue_buffer = NULL; |
---|
| 326 | wlan_exp_transport_header* transport_header_rx = NULL; |
---|
| 327 | wlan_exp_transport_header* transport_header_tx = NULL; |
---|
| 328 | dl_entry* eth_tx_queue_entry; |
---|
| 329 | |
---|
| 330 | resp_sent = NO_RESP_SENT; |
---|
| 331 | |
---|
| 332 | transport_header_rx = (wlan_exp_transport_header*)( wlan_exp_eth_rx_queue_buffer->pkt |
---|
| 333 | + sizeof(ethernet_header_t) |
---|
| 334 | + sizeof(ipv4_header_t) |
---|
| 335 | + sizeof(udp_header_t) ); |
---|
| 336 | |
---|
| 337 | // Extract values from the received transport header |
---|
| 338 | // |
---|
| 339 | dest_id = Xil_Ntohs(transport_header_rx->dest_id); |
---|
| 340 | src_id = Xil_Ntohs(transport_header_rx->src_id); |
---|
| 341 | seq_num = Xil_Ntohs(transport_header_rx->seq_num); |
---|
| 342 | flags = Xil_Ntohs(transport_header_rx->flags); |
---|
| 343 | |
---|
| 344 | node_id = wlan_exp_node_info.node_id; |
---|
| 345 | group_id = wlan_exp_transport_info.group_id; |
---|
| 346 | |
---|
| 347 | // If this message is not for the given node, then ignore it |
---|
| 348 | if((dest_id != node_id) && (dest_id != TRANSPORT_BROADCAST_DEST_ID) && ((dest_id & (0xFF00 | group_id)) == 0)) { return; } |
---|
| 349 | |
---|
| 350 | // We can now construct an outgoing response template |
---|
| 351 | eth_tx_queue_entry = queue_checkout(); |
---|
| 352 | |
---|
| 353 | if(eth_tx_queue_entry == NULL){ |
---|
| 354 | xil_printf("Unable to checkout free queue entry for wlan_exp Eth response\n"); |
---|
| 355 | return; |
---|
| 356 | } |
---|
| 357 | |
---|
| 358 | eth_tx_queue_buffer = (eth_tx_queue_buffer_t*)eth_tx_queue_entry->data; |
---|
| 359 | |
---|
| 360 | // Update segment 0 length to account for Eth/IP/UDP headers |
---|
| 361 | eth_tx_queue_buffer->seg0_len = (sizeof(ethernet_header_t) |
---|
| 362 | + sizeof(ipv4_header_t) |
---|
| 363 | + sizeof(udp_header_t)); |
---|
| 364 | eth_tx_queue_buffer->seg1_len = 0; |
---|
| 365 | |
---|
| 366 | // The response template has already been filled in with an Ethernet, IP, and UDP header. |
---|
| 367 | // we will point to the bytes after these headers to place the transport header. |
---|
| 368 | transport_header_tx = (wlan_exp_transport_header*)(eth_tx_queue_buffer->seg0 + eth_tx_queue_buffer->seg0_len); |
---|
| 369 | |
---|
| 370 | eth_tx_queue_buffer->seg0_len += sizeof(wlan_exp_transport_header); |
---|
| 371 | |
---|
| 372 | resp_template_length = eth_tx_queue_buffer->seg0_len; |
---|
| 373 | |
---|
| 374 | |
---|
| 375 | // Form outgoing Transport header for any outgoing packet in response to this message |
---|
| 376 | // The u16/u32 fields here will be endian swapped in wlan_exp_transport_send |
---|
| 377 | // The length field of the header will be set in wlan_exp_transport_send |
---|
| 378 | // |
---|
| 379 | |
---|
| 380 | transport_header_tx->dest_id = src_id; |
---|
| 381 | transport_header_tx->src_id = node_id; |
---|
| 382 | transport_header_tx->seq_num = seq_num; |
---|
| 383 | transport_header_tx->flags = 0; |
---|
| 384 | |
---|
| 385 | // Call the callback to further process the recv_buffer |
---|
| 386 | resp_sent = process_msg_from_host(wlan_exp_eth_rx_queue_buffer, eth_tx_queue_buffer); |
---|
| 387 | |
---|
| 388 | // Based on the status, return a message to the host |
---|
| 389 | if(resp_sent == NO_RESP_SENT) { |
---|
| 390 | // Check if the host requires a response from the node |
---|
| 391 | if (flags & TRANSPORT_HDR_ROBUST_FLAG ) { |
---|
| 392 | |
---|
| 393 | if( (eth_tx_queue_buffer->seg0_len) > resp_template_length ){ |
---|
| 394 | // Check that the node has something to send to the host |
---|
| 395 | if ( (wlan_exp_transport_fill_headers_response(eth_tx_queue_buffer) == 0) ) { |
---|
| 396 | wlan_exp_transport_send(eth_tx_queue_buffer); |
---|
| 397 | return; |
---|
| 398 | } |
---|
| 399 | }else { |
---|
| 400 | wlan_exp_printf(WLAN_EXP_PRINT_WARNING, print_type_transport, "Host requires response but node has nothing to send.\n"); |
---|
| 401 | } |
---|
| 402 | } |
---|
| 403 | queue_checkin(eth_tx_queue_entry); |
---|
| 404 | } |
---|
| 405 | |
---|
| 406 | return; |
---|
| 407 | } |
---|
| 408 | |
---|
| 409 | |
---|
| 410 | |
---|
| 411 | /*****************************************************************************/ |
---|
| 412 | /** |
---|
| 413 | * @brief Finish response packet and enqueue for transmission |
---|
| 414 | * |
---|
| 415 | * @param eth_tx_queue_buffer_t* eth_tx_queue_buffer - packet queue buffer for outgoing Ethernet frame |
---|
| 416 | * |
---|
| 417 | * @return None |
---|
| 418 | * |
---|
| 419 | *****************************************************************************/ |
---|
| 420 | void wlan_exp_transport_send(eth_tx_queue_buffer_t* eth_tx_queue_buffer) { |
---|
| 421 | wlan_exp_transport_prepare_headers(eth_tx_queue_buffer); |
---|
| 422 | wlan_exp_append_tx(eth_tx_queue_buffer->pyld_queue_hdr.dle); |
---|
| 423 | } |
---|
| 424 | |
---|
| 425 | /*****************************************************************************/ |
---|
| 426 | /** |
---|
| 427 | * @brief Prepare headers in response packet for transmission |
---|
| 428 | * |
---|
| 429 | * This function will swap endianness of the wlan_exp_transport_header portion |
---|
| 430 | * of the provided packet queue buffer, set the IP checksum for the finalized |
---|
| 431 | * IP header contents, and enqueue the packet for transmission. |
---|
| 432 | * |
---|
| 433 | * @param eth_tx_queue_buffer_t* eth_tx_queue_buffer - packet queue buffer for outgoing Ethernet frame |
---|
| 434 | * |
---|
| 435 | * @return None |
---|
| 436 | * |
---|
| 437 | *****************************************************************************/ |
---|
| 438 | void wlan_exp_transport_prepare_headers(eth_tx_queue_buffer_t* eth_tx_queue_buffer){ |
---|
| 439 | wlan_exp_transport_header* transport_header_tx; |
---|
| 440 | |
---|
| 441 | transport_header_tx = (wlan_exp_transport_header*)(eth_tx_queue_buffer->seg0 |
---|
| 442 | + sizeof(ethernet_header_t) |
---|
| 443 | + sizeof(ipv4_header_t) |
---|
| 444 | + sizeof(udp_header_t)); |
---|
| 445 | |
---|
| 446 | transport_header_tx->dest_id = Xil_Htons(transport_header_tx->dest_id); |
---|
| 447 | transport_header_tx->src_id = Xil_Htons(transport_header_tx->src_id); |
---|
| 448 | |
---|
| 449 | // Transport header's length includes all data following the Eth/IP/UDP headers |
---|
| 450 | transport_header_tx->length = Xil_Htons(eth_tx_queue_buffer->seg0_len |
---|
| 451 | + eth_tx_queue_buffer->seg1_len |
---|
| 452 | - sizeof(ethernet_header_t) |
---|
| 453 | - sizeof(ipv4_header_t) |
---|
| 454 | - sizeof(udp_header_t)); |
---|
| 455 | transport_header_tx->seq_num = Xil_Htons(transport_header_tx->seq_num); |
---|
| 456 | transport_header_tx->flags = Xil_Htons(transport_header_tx->flags); |
---|
| 457 | |
---|
| 458 | // Set the IP/UDP header lengths and update checksum |
---|
| 459 | wlan_exp_ip_udp_set_length(eth_tx_queue_buffer->seg0, eth_tx_queue_buffer->seg0_len + eth_tx_queue_buffer->seg1_len); |
---|
| 460 | |
---|
| 461 | } |
---|
| 462 | |
---|
| 463 | |
---|
| 464 | /*****************************************************************************/ |
---|
| 465 | /** |
---|
| 466 | * @brief Process Transport Commands |
---|
| 467 | * |
---|
| 468 | * Process commands from a host meant for the transport group |
---|
| 469 | * |
---|
| 470 | * @param cmd_hdr - pointer to the command header |
---|
| 471 | * @param eth_tx_queue_buffer - pointer to a Ethernet queue buffer that should be |
---|
| 472 | * filled in with response arguments |
---|
| 473 | * |
---|
| 474 | * @return int - NO_RESP_SENT or RESP_SENT |
---|
| 475 | * |
---|
| 476 | *****************************************************************************/ |
---|
| 477 | int process_transport_cmd(cmd_resp_hdr_t* cmd_hdr, eth_tx_queue_buffer_t* eth_tx_queue_buffer) { |
---|
| 478 | |
---|
| 479 | // |
---|
| 480 | // IMPORTANT ENDIAN NOTES: |
---|
| 481 | // - command |
---|
| 482 | // - header - Already endian swapped by the framework (safe to access directly) |
---|
| 483 | // - args - Must be endian swapped as necessary by code (framework does not know the contents of the command) |
---|
| 484 | // - response |
---|
| 485 | // - header - Will be endian swapped by the framework (safe to write directly) |
---|
| 486 | // - args - Must be endian swapped as necessary by code (framework does not know the contents of the response) |
---|
| 487 | // |
---|
| 488 | |
---|
| 489 | // Standard variables |
---|
| 490 | u32 resp_sent = NO_RESP_SENT; |
---|
| 491 | |
---|
| 492 | u32 cmd_id = CMD_TO_CMDID(cmd_hdr->cmd); |
---|
| 493 | |
---|
| 494 | // Segment 0 length includes a fully formed command response header |
---|
| 495 | // because one was created with default values suitable for a responseless |
---|
| 496 | // acknowledgment. |
---|
| 497 | cmd_resp_hdr_t* resp_hdr = (cmd_resp_hdr_t*)(eth_tx_queue_buffer->seg0 |
---|
| 498 | + eth_tx_queue_buffer->seg0_len |
---|
| 499 | - sizeof(cmd_resp_hdr_t)); |
---|
| 500 | |
---|
| 501 | u32* cmd_args_32 = (u32*)((u8*)cmd_hdr + sizeof(cmd_resp_hdr_t)); |
---|
| 502 | |
---|
| 503 | // Process the command |
---|
| 504 | switch(cmd_id){ |
---|
| 505 | |
---|
| 506 | //--------------------------------------------------------------------- |
---|
| 507 | case CMDID_TRANSPORT_PING: { |
---|
| 508 | // |
---|
| 509 | // Nothing actually needs to be done when receiving the ping command. The framework is going |
---|
| 510 | // to respond regardless, which is all the host wants. |
---|
| 511 | // |
---|
| 512 | } |
---|
| 513 | break; |
---|
| 514 | |
---|
| 515 | //--------------------------------------------------------------------- |
---|
| 516 | case CMDID_TRANSPORT_TEST_MTU: { |
---|
| 517 | // Host requests a packet with a bogus payload with a specified length |
---|
| 518 | // Python already accounts for the transport and cmd-response headers |
---|
| 519 | // in computing the requested length to achieve the desired over-the-wire length |
---|
| 520 | |
---|
| 521 | u32 req_length = Xil_Ntohl(cmd_args_32[0]); |
---|
| 522 | |
---|
| 523 | // Copy the requested length as the only response argument |
---|
| 524 | // Python can compare this value to the requested length to confirm |
---|
| 525 | // this is a valid response, then examine the actual length of the |
---|
| 526 | // received packet to confirm the effective MTU |
---|
| 527 | wlan_exp_add_u32_resp_arg(eth_tx_queue_buffer, resp_hdr, req_length); |
---|
| 528 | |
---|
| 529 | // This command response header is unusual as it includes a single argument |
---|
| 530 | // (the requested response payload length) but a large payload that isn't |
---|
| 531 | // a "response argument". The large payload is bogus, only used to test MTU |
---|
| 532 | // along the node-to-host link. Other command/response code should *not* |
---|
| 533 | // mimic this unusual num_args/length mismatch |
---|
| 534 | // The seg1_addr value below can be any DMA-accessible memory area |
---|
| 535 | resp_hdr->length += req_length; |
---|
| 536 | eth_tx_queue_buffer->seg1_addr = (u8*)USER_SCRATCH_BASE; |
---|
| 537 | eth_tx_queue_buffer->seg1_len = req_length; |
---|
| 538 | } |
---|
| 539 | break; |
---|
| 540 | |
---|
| 541 | //--------------------------------------------------------------------- |
---|
| 542 | case CMDID_TRANSPORT_SET_MAX_RESP_WORDS: { |
---|
| 543 | u32 max_words = Xil_Ntohl(cmd_args_32[0]); |
---|
| 544 | |
---|
| 545 | // The wlan_exp host sets this node's maximum payload size for response packets |
---|
| 546 | // The host determines this maximum based on its own configuration (host NIC MTU), |
---|
| 547 | // the MTU reported by this node during init (node_info.wlan_exp_eth_mtu) and the |
---|
| 548 | // result of the MTU test run during init. |
---|
| 549 | // The C code keeps two MTU values at runtime: |
---|
| 550 | // - wlan_exp_node_info.wlan_exp_eth_mtu: the MTU in bytes of the node's Eth interface, set once at boot |
---|
| 551 | // Almost always 1500 (non-jumbo) or 9000 (jumbo) |
---|
| 552 | // |
---|
| 553 | // - wlan_exp_transport_info.max_pkt_words: the maximum number of u32 payload words the node may packet |
---|
| 554 | // into a response packet sent to the host. This value describes only the wlan_exp payload, not the |
---|
| 555 | // IP/UDP/wlan_exp headers that precede the payload on the wire |
---|
| 556 | |
---|
| 557 | // This is a blind setter - extra error checking here might be sensible, but this command |
---|
| 558 | // is currently only called after the MTU test, which itself has lots of error checking and printing |
---|
| 559 | transport_set_max_resp_pkt_words(max_words); |
---|
| 560 | } |
---|
| 561 | break; |
---|
| 562 | |
---|
| 563 | //--------------------------------------------------------------------- |
---|
| 564 | case CMDID_TRANSPORT_NODE_GROUP_ID_ADD: { |
---|
| 565 | wlan_exp_transport_info.group_id = (wlan_exp_transport_info.group_id | Xil_Htonl(cmd_args_32[0])); |
---|
| 566 | } |
---|
| 567 | break; |
---|
| 568 | |
---|
| 569 | //--------------------------------------------------------------------- |
---|
| 570 | case CMDID_TRANSPORT_NODE_GROUP_ID_CLEAR: { |
---|
| 571 | wlan_exp_transport_info.group_id = (wlan_exp_transport_info.group_id & ~Xil_Htonl(cmd_args_32[0])); |
---|
| 572 | } |
---|
| 573 | break; |
---|
| 574 | |
---|
| 575 | //--------------------------------------------------------------------- |
---|
| 576 | default: { |
---|
| 577 | wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Unknown user command ID: %d\n", cmd_id); |
---|
| 578 | } |
---|
| 579 | break; |
---|
| 580 | } |
---|
| 581 | |
---|
| 582 | return resp_sent; |
---|
| 583 | } |
---|
| 584 | |
---|
| 585 | /*****************************************************************************/ |
---|
| 586 | /** |
---|
| 587 | * @brief Configure unicast and broadcast sockets |
---|
| 588 | * |
---|
| 589 | * @param unicast_port - Unicast port for the node |
---|
| 590 | * @param broadcast_port - Broadcast port for the node |
---|
| 591 | * |
---|
| 592 | * @return int - WLAN_SUCCESS or WLAN_FAILURE |
---|
| 593 | * |
---|
| 594 | *****************************************************************************/ |
---|
| 595 | int transport_config_sockets(u32 unicast_port, u32 broadcast_port) { |
---|
| 596 | int status = WLAN_SUCCESS; |
---|
| 597 | |
---|
| 598 | status = _transport_config_socket(&(wlan_exp_transport_info.socket_unicast), unicast_port); |
---|
| 599 | if (status == WLAN_FAILURE) { return status; } |
---|
| 600 | wlan_exp_transport_info.unicast_port = unicast_port; |
---|
| 601 | |
---|
| 602 | status = _transport_config_socket(&(wlan_exp_transport_info.socket_broadcast), broadcast_port); |
---|
| 603 | if (status == WLAN_FAILURE) { return status; } |
---|
| 604 | wlan_exp_transport_info.broadcast_port = broadcast_port; |
---|
| 605 | |
---|
| 606 | |
---|
| 607 | xil_printf(" Listening on UDP ports %d (unicast) and %d (broadcast)\n", unicast_port, broadcast_port); |
---|
| 608 | |
---|
| 609 | |
---|
| 610 | return status; |
---|
| 611 | } |
---|
| 612 | |
---|
| 613 | |
---|
| 614 | /*****************************************************************************/ |
---|
| 615 | /** |
---|
| 616 | * @brief Set the IP address of the node |
---|
| 617 | * |
---|
| 618 | * @return None |
---|
| 619 | * |
---|
| 620 | *****************************************************************************/ |
---|
| 621 | void transport_set_ip_addr(u8* ip_addr){ |
---|
| 622 | |
---|
| 623 | eth_set_ip_addr( ip_addr ); |
---|
| 624 | |
---|
| 625 | return; |
---|
| 626 | } |
---|
| 627 | |
---|
| 628 | /*****************************************************************************/ |
---|
| 629 | /** |
---|
| 630 | * @brief Get the IP address of the node |
---|
| 631 | * |
---|
| 632 | * @return None |
---|
| 633 | * |
---|
| 634 | *****************************************************************************/ |
---|
| 635 | void transport_get_ip_addr(u8 * ip_addr) { |
---|
| 636 | |
---|
| 637 | eth_get_ip_addr( ip_addr ); |
---|
| 638 | |
---|
| 639 | return; |
---|
| 640 | } |
---|
| 641 | |
---|
| 642 | /*****************************************************************************/ |
---|
| 643 | /** |
---|
| 644 | * @brief Get the maximum number of u32 words this transport is able to send |
---|
| 645 | * in a single packet |
---|
| 646 | * |
---|
| 647 | * @return u32 - number of u32 words |
---|
| 648 | * |
---|
| 649 | *****************************************************************************/ |
---|
| 650 | u32 wlan_exp_transport_get_max_pkt_words(){ |
---|
| 651 | return wlan_exp_transport_info.max_pkt_words; |
---|
| 652 | } |
---|
| 653 | |
---|
| 654 | /*****************************************************************************/ |
---|
| 655 | /** |
---|
| 656 | * @brief Set the maximum number of u32 words this transport is able to send |
---|
| 657 | * in a single packet |
---|
| 658 | * |
---|
| 659 | * @param max_words - maximum number of u32 words |
---|
| 660 | * @return None |
---|
| 661 | * |
---|
| 662 | *****************************************************************************/ |
---|
| 663 | void transport_set_max_resp_pkt_words(u32 max_words) { |
---|
| 664 | // Update the maximum size for response packet payloads |
---|
| 665 | // Used to reset the maximum on node init and to update |
---|
| 666 | // the maximum after the MTU test |
---|
| 667 | wlan_exp_transport_info.max_pkt_words = max_words; |
---|
| 668 | } |
---|
| 669 | |
---|
| 670 | // Local functions |
---|
| 671 | /*****************************************************************************/ |
---|
| 672 | /** |
---|
| 673 | * @brief Update software interrupt signal based on Tx queue occupancy |
---|
| 674 | * |
---|
| 675 | * @param queue_len - current length of the Tx queue |
---|
| 676 | * @return None |
---|
| 677 | * |
---|
| 678 | *****************************************************************************/ |
---|
| 679 | void _wlan_exp_tx_queue_occupancy_change(int callback_arg, u32 queue_len){ |
---|
| 680 | // callback_arg is not used. It was set to 0 when opening the queue |
---|
| 681 | |
---|
| 682 | if(queue_len == 0){ |
---|
| 683 | wlan_platform_clear_sw_intr(SW_INTR_ID_WLAN_EXP_ETH_TX); |
---|
| 684 | } else { |
---|
| 685 | wlan_platform_assert_sw_intr(SW_INTR_ID_WLAN_EXP_ETH_TX); |
---|
| 686 | } |
---|
| 687 | } |
---|
| 688 | |
---|
| 689 | /*****************************************************************************/ |
---|
| 690 | /** |
---|
| 691 | * @brief Create and bind a socket |
---|
| 692 | * |
---|
| 693 | * @param socket_index - Socket index (return value) |
---|
| 694 | * @param udp_port - UDP port number |
---|
| 695 | * |
---|
| 696 | * @return int - WLAN_SUCCESS or WLAN_FAILURE |
---|
| 697 | * |
---|
| 698 | *****************************************************************************/ |
---|
| 699 | int _transport_config_socket(int* socket_index, u32 udp_port) { |
---|
| 700 | |
---|
| 701 | int status; |
---|
| 702 | int tmp_socket = *socket_index; |
---|
| 703 | |
---|
| 704 | // Release socket if it is already bound |
---|
| 705 | if (tmp_socket != SOCKET_INVALID_SOCKET) { |
---|
| 706 | socket_close(tmp_socket); |
---|
| 707 | } |
---|
| 708 | |
---|
| 709 | // Create a new socket |
---|
| 710 | tmp_socket = socket_alloc(AF_INET, SOCK_DGRAM, 0); |
---|
| 711 | |
---|
| 712 | if (tmp_socket == SOCKET_INVALID_SOCKET) { |
---|
| 713 | wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Could not create socket\n"); |
---|
| 714 | *socket_index = SOCKET_INVALID_SOCKET; |
---|
| 715 | return WLAN_FAILURE; |
---|
| 716 | } |
---|
| 717 | |
---|
| 718 | // Bind the socket |
---|
| 719 | status = socket_bind_eth(tmp_socket, udp_port); |
---|
| 720 | |
---|
| 721 | if (status == WLAN_FAILURE) { |
---|
| 722 | wlan_exp_printf(WLAN_EXP_PRINT_ERROR, print_type_transport, "Unable to bind socket on port: %d\n", udp_port); |
---|
| 723 | socket_close(tmp_socket); |
---|
| 724 | *socket_index = SOCKET_INVALID_SOCKET; |
---|
| 725 | return WLAN_FAILURE; |
---|
| 726 | } |
---|
| 727 | |
---|
| 728 | *socket_index = tmp_socket; |
---|
| 729 | |
---|
| 730 | return WLAN_SUCCESS; |
---|
| 731 | } |
---|
| 732 | |
---|
| 733 | |
---|
| 734 | #endif |
---|