1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** NetX Component */
17 /** */
18 /** Transmission Control Protocol (TCP) */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23
24
25 #define NX_SOURCE_CODE
26
27
28 /* Include necessary system files. */
29
30 #include "nx_api.h"
31 #include "nx_ip.h"
32 #include "nx_packet.h"
33 #include "nx_tcp.h"
34 #include "tx_thread.h"
35
36
37 /**************************************************************************/
38 /* */
39 /* FUNCTION RELEASE */
40 /* */
41 /* _nx_tcp_socket_receive PORTABLE C */
42 /* 6.1 */
43 /* AUTHOR */
44 /* */
45 /* Yuxin Zhou, Microsoft Corporation */
46 /* */
47 /* DESCRIPTION */
48 /* */
49 /* This function attempts to receive one or more TCP packets from the */
50 /* specified socket. */
51 /* */
52 /* INPUT */
53 /* */
54 /* socket_ptr Pointer to socket */
55 /* packet_ptr Pointer to packet pointer */
56 /* wait_option Suspension option */
57 /* */
58 /* OUTPUT */
59 /* */
60 /* status Completion status */
61 /* */
62 /* CALLS */
63 /* */
64 /* _nx_tcp_packet_send_ack Send ACK message */
65 /* _nx_tcp_socket_thread_suspend Suspend calling thread */
66 /* tx_mutex_get Get protection mutex */
67 /* tx_mutex_put Put protection mutex */
68 /* */
69 /* CALLED BY */
70 /* */
71 /* Application */
72 /* */
73 /* RELEASE HISTORY */
74 /* */
75 /* DATE NAME DESCRIPTION */
76 /* */
77 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
78 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
79 /* resulting in version 6.1 */
80 /* */
81 /**************************************************************************/
_nx_tcp_socket_receive(NX_TCP_SOCKET * socket_ptr,NX_PACKET ** packet_ptr,ULONG wait_option)82 UINT _nx_tcp_socket_receive(NX_TCP_SOCKET *socket_ptr, NX_PACKET **packet_ptr, ULONG wait_option)
83 {
84
85 NX_IP *ip_ptr;
86 NX_TCP_HEADER *header_ptr;
87 NX_PACKET *head_packet_ptr;
88 ULONG header_length;
89
90 #ifdef TX_ENABLE_EVENT_TRACE
91 TX_TRACE_BUFFER_ENTRY *trace_event;
92 ULONG trace_timestamp;
93 #endif
94
95 /* Setup the pointer to the associated IP instance. */
96 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
97
98 /* Set the return pointer to NULL initially. */
99 *packet_ptr = NX_NULL;
100
101 /* If trace is enabled, insert this event into the trace buffer. */
102 NX_TRACE_IN_LINE_INSERT(NX_TRACE_TCP_SOCKET_RECEIVE, socket_ptr, 0, 0, 0, NX_TRACE_TCP_EVENTS, &trace_event, &trace_timestamp);
103
104 /* Get protection while we look at this socket. */
105 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
106
107 /* Determine if the socket is currently bound. */
108 if (!socket_ptr -> nx_tcp_socket_bound_next)
109 {
110
111 /* Release protection. */
112 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
113
114 /* Socket is not bound, return an error message. */
115 return(NX_NOT_BOUND);
116 }
117
118 /* Do not return without data if there is data on the queue. */
119 if (!socket_ptr -> nx_tcp_socket_receive_queue_head)
120 {
121 /* There is no data on the queue. */
122
123 /* Determine if the socket is still in an active state, but also allow
124 a receive socket operation if there are still more queued receive
125 packets for this socket. */
126 if ((socket_ptr -> nx_tcp_socket_state < NX_TCP_SYN_SENT) ||
127 (socket_ptr -> nx_tcp_socket_state == NX_TCP_CLOSE_WAIT) ||
128 (socket_ptr -> nx_tcp_socket_state >= NX_TCP_CLOSING))
129 {
130
131 /* Release the IP protection. */
132 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
133
134 /* Return an error code. */
135 return(NX_NOT_CONNECTED);
136 }
137 }
138
139 /* Pickup the important information from the socket. */
140
141 /* Attempt to build a pointer to the first packet in the socket's
142 receive queue. */
143 if (socket_ptr -> nx_tcp_socket_receive_queue_head)
144 {
145
146 /* Yes, there is a packet on the receive queue. Setup a pointer to it and
147 its header. */
148 head_packet_ptr = socket_ptr -> nx_tcp_socket_receive_queue_head;
149 }
150 else
151 {
152
153 /* Just set the pointers to NULL. */
154 head_packet_ptr = NX_NULL;
155 }
156
157 /* Determine if there is a receive packet available. */
158 /*lint -e{923} suppress cast of ULONT to pointer. */
159 if ((head_packet_ptr) && (head_packet_ptr -> nx_packet_queue_next == ((NX_PACKET *)NX_PACKET_READY)))
160 {
161
162
163 /* Yes, the first packet in the queue is available and has been ACKed. Remove it
164 from the queue and return it to the caller. */
165 if (head_packet_ptr == socket_ptr -> nx_tcp_socket_receive_queue_tail)
166 {
167
168 /* Only item in the queue. Set the head and tail pointers to NULL. */
169 socket_ptr -> nx_tcp_socket_receive_queue_head = NX_NULL;
170 socket_ptr -> nx_tcp_socket_receive_queue_tail = NX_NULL;
171 }
172 else
173 {
174
175 /* Simply update the head pointer to the packet after the current. The tail pointer does not
176 need update. */
177 socket_ptr -> nx_tcp_socket_receive_queue_head = head_packet_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next;
178 }
179
180 /* Decrease the number of received packets. */
181 socket_ptr -> nx_tcp_socket_receive_queue_count--;
182
183 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
184 header_ptr = (NX_TCP_HEADER *)head_packet_ptr -> nx_packet_prepend_ptr;
185
186 /* Calculate the header size for this packet. */
187 header_length = (header_ptr -> nx_tcp_header_word_3 >> NX_TCP_HEADER_SHIFT) * (ULONG)sizeof(ULONG);
188
189 /* Adjust the packet prepend pointer and length to position past the TCP header. */
190 head_packet_ptr -> nx_packet_prepend_ptr = head_packet_ptr -> nx_packet_prepend_ptr + header_length;
191 head_packet_ptr -> nx_packet_length = head_packet_ptr -> nx_packet_length - header_length;
192
193 /* Indicate that this TCP packet is no longer enqueued by marking it again as allocated. This is what
194 it was prior to being part of the TCP receive queue. */
195 /*lint -e{923} suppress cast of ULONT to pointer. */
196 head_packet_ptr -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ALLOCATED;
197
198 /* Clear the queue next pointer. */
199 head_packet_ptr -> nx_packet_queue_next = NX_NULL;
200
201 /* Place the packet pointer in the return pointer. */
202 *packet_ptr = head_packet_ptr;
203
204 /* Check the receive queue count. */
205 if (socket_ptr -> nx_tcp_socket_receive_queue_count == 0)
206 {
207
208 /* Make sure the current receive window is the default window! */
209 socket_ptr -> nx_tcp_socket_rx_window_current = socket_ptr -> nx_tcp_socket_rx_window_default;
210 }
211 else
212 {
213
214 /* Increase the receive window size. */
215 socket_ptr -> nx_tcp_socket_rx_window_current += (*packet_ptr) -> nx_packet_length;
216 }
217
218 /* Determine if an ACK should be forced out for window update, SWS avoidance algorithm.
219 RFC1122, Section4.2.3.3, Page97-98. */
220 if (((socket_ptr -> nx_tcp_socket_rx_window_current - socket_ptr -> nx_tcp_socket_rx_window_last_sent) >= (socket_ptr -> nx_tcp_socket_rx_window_default / 2)) &&
221 ((socket_ptr -> nx_tcp_socket_state == NX_TCP_ESTABLISHED) || (socket_ptr -> nx_tcp_socket_state == NX_TCP_FIN_WAIT_1) || (socket_ptr -> nx_tcp_socket_state == NX_TCP_FIN_WAIT_2)))
222 {
223
224 /* Send a Window Update. */
225 _nx_tcp_packet_send_ack(socket_ptr, socket_ptr -> nx_tcp_socket_tx_sequence);
226 }
227
228 #ifdef TX_ENABLE_EVENT_TRACE
229 /* Update the trace event with the status. */
230 NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_TCP_SOCKET_RECEIVE, 0, *packet_ptr, (*packet_ptr) -> nx_packet_length, socket_ptr -> nx_tcp_socket_rx_sequence);
231 #endif /* TX_ENABLE_EVENT_TRACE */
232
233 /* Release protection. */
234 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
235
236 /* Return a successful status. */
237 return(NX_SUCCESS);
238 }
239 else if ((wait_option) && (_tx_thread_current_ptr != &(ip_ptr -> nx_ip_thread)))
240 {
241
242 /* Suspend the thread on this socket's receive queue. */
243
244 /* Save the return packet pointer address as well. */
245 _tx_thread_current_ptr -> tx_thread_additional_suspend_info = (void *)packet_ptr;
246
247 /* Increment the suspended thread count. */
248 socket_ptr -> nx_tcp_socket_receive_suspended_count++;
249
250 /* Suspend the thread on the receive queue. */
251 /* Note that the mutex is released inside _nx_tcp_socket_thread_suspend(). */
252 _nx_tcp_socket_thread_suspend(&(socket_ptr -> nx_tcp_socket_receive_suspension_list), _nx_tcp_receive_cleanup, socket_ptr, &(ip_ptr -> nx_ip_protection), wait_option);
253 #ifdef TX_ENABLE_EVENT_TRACE
254 if (*packet_ptr)
255 {
256
257 /* Update the trace event with the status. */
258 NX_TRACE_EVENT_UPDATE(trace_event, trace_timestamp, NX_TRACE_TCP_SOCKET_RECEIVE, 0, *packet_ptr, (*packet_ptr) -> nx_packet_length, socket_ptr -> nx_tcp_socket_rx_sequence);
259 }
260 #endif /* TX_ENABLE_EVENT_TRACE */
261 /* If not, just return the error code. */
262 return(_tx_thread_current_ptr -> tx_thread_suspend_status);
263 }
264 else
265 {
266
267 /* Release protection. */
268 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
269
270 /* Return an empty receive queue error message. */
271 return(NX_NO_PACKET);
272 }
273 }
274
275