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 |
---|