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 /** User Datagram Protocol (UDP) */
19 /** */
20 /**************************************************************************/
21 /**************************************************************************/
22
23 #define NX_SOURCE_CODE
24
25
26 /* Include necessary system files. */
27
28 #include "nx_api.h"
29 #include "nx_udp.h"
30 #include "nx_packet.h"
31 #include "tx_thread.h"
32
33
34 #ifdef NX_ENABLE_TCPIP_OFFLOAD
35 /**************************************************************************/
36 /* */
37 /* FUNCTION RELEASE */
38 /* */
39 /* _nx_udp_socket_driver_unbind PORTABLE C */
40 /* 6.1.8 */
41 /* AUTHOR */
42 /* */
43 /* Yuxin Zhou, Microsoft Corporation */
44 /* */
45 /* DESCRIPTION */
46 /* */
47 /* This function unbinds a UDP port through TCP/IP offload interface. */
48 /* */
49 /* INPUT */
50 /* */
51 /* socket_ptr Pointer to UDP socket */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* status Completion status */
56 /* */
57 /* CALLS */
58 /* */
59 /* None */
60 /* */
61 /* CALLED BY */
62 /* */
63 /* _nx_udp_socket_bind */
64 /* */
65 /* RELEASE HISTORY */
66 /* */
67 /* DATE NAME DESCRIPTION */
68 /* */
69 /* 08-02-2021 Yuxin Zhou Initial Version 6.1.8 */
70 /* */
71 /**************************************************************************/
_nx_udp_socket_driver_unbind(NX_UDP_SOCKET * socket_ptr)72 static UINT _nx_udp_socket_driver_unbind(NX_UDP_SOCKET *socket_ptr)
73 {
74 UINT i;
75 NX_INTERFACE *interface_ptr;
76 NX_IP *ip_ptr;
77
78
79 /* Setup the pointer to the associated IP instance. */
80 ip_ptr = socket_ptr -> nx_udp_socket_ip_ptr;
81
82 /* Loop all interfaces to unbind to ones support TCP/IP offload. */
83 for (i = 0; i < NX_MAX_IP_INTERFACES; i++)
84 {
85
86 /* Use a local variable for convenience. */
87 interface_ptr = &(ip_ptr -> nx_ip_interface[i]);
88
89 /* Check for valid interfaces. */
90 if (interface_ptr -> nx_interface_valid == NX_FALSE)
91 {
92
93 /* Skip interface not valid. */
94 continue;
95 }
96
97 /* Check for TCP/IP offload feature. */
98 if (((interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) == 0) ||
99 (interface_ptr -> nx_interface_tcpip_offload_handler == NX_NULL))
100 {
101
102 /* Skip interface not support TCP/IP offload. */
103 continue;
104 }
105
106 /* Let TCP/IP offload interface unbind port. Return value is ignored. */
107 interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr,
108 socket_ptr,
109 NX_TCPIP_OFFLOAD_UDP_SOCKET_UNBIND,
110 NX_NULL, NX_NULL, NX_NULL,
111 socket_ptr -> nx_udp_socket_port,
112 NX_NULL, NX_NO_WAIT);
113 }
114
115 return(NX_SUCCESS);
116 }
117 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
118
119
120 /**************************************************************************/
121 /* */
122 /* FUNCTION RELEASE */
123 /* */
124 /* _nx_udp_socket_unbind PORTABLE C */
125 /* 6.1.8 */
126 /* AUTHOR */
127 /* */
128 /* Yuxin Zhou, Microsoft Corporation */
129 /* */
130 /* DESCRIPTION */
131 /* */
132 /* This function unbinds the UDP socket structure from the previously */
133 /* bound UDP port. */
134 /* */
135 /* INPUT */
136 /* */
137 /* socket_ptr Pointer to UDP socket */
138 /* */
139 /* OUTPUT */
140 /* */
141 /* status Completion status */
142 /* */
143 /* CALLS */
144 /* */
145 /* _nx_packet_release Release data packet */
146 /* _nx_udp_bind_cleanup Remove and cleanup bind req */
147 /* tx_mutex_get Obtain protection mutex */
148 /* tx_mutex_put Release protection mutex */
149 /* _tx_thread_system_resume Resume suspended thread */
150 /* _tx_thread_system_preempt_check Check for preemption */
151 /* */
152 /* CALLED BY */
153 /* */
154 /* Application Code */
155 /* */
156 /* RELEASE HISTORY */
157 /* */
158 /* DATE NAME DESCRIPTION */
159 /* */
160 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
161 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
162 /* resulting in version 6.1 */
163 /* 08-02-2021 Yuxin Zhou Modified comment(s), and */
164 /* supported TCP/IP offload, */
165 /* resulting in version 6.1.8 */
166 /* */
167 /**************************************************************************/
_nx_udp_socket_unbind(NX_UDP_SOCKET * socket_ptr)168 UINT _nx_udp_socket_unbind(NX_UDP_SOCKET *socket_ptr)
169 {
170 TX_INTERRUPT_SAVE_AREA
171
172 UINT index;
173 UINT port;
174 NX_IP *ip_ptr;
175 TX_THREAD *thread_ptr;
176 NX_UDP_SOCKET *new_socket_ptr;
177 NX_PACKET *packet_ptr;
178 NX_PACKET *next_packet_ptr;
179
180
181 /* Setup the pointer to the associated IP instance. */
182 ip_ptr = socket_ptr -> nx_udp_socket_ip_ptr;
183
184 /* If trace is enabled, insert this event into the trace buffer. */
185 NX_TRACE_IN_LINE_INSERT(NX_TRACE_UDP_SOCKET_UNBIND, ip_ptr, socket_ptr, socket_ptr -> nx_udp_socket_port, 0, NX_TRACE_UDP_EVENTS, 0, 0);
186
187 /* Obtain the IP mutex so we can figure out whether or not the port has already
188 been bound to. */
189 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
190
191 /* Determine if the socket is bound to port. */
192 if (!socket_ptr -> nx_udp_socket_bound_next)
193 {
194
195 /* Determine if there is a special condition for the socket not being in
196 a bound condition... i.e. the socket is in a pending-to-be-bound condition
197 in a call from a different thread. */
198 if (socket_ptr -> nx_udp_socket_bind_in_progress)
199 {
200
201 /* Execute the bind suspension cleanup routine. */
202 _nx_udp_bind_cleanup(socket_ptr -> nx_udp_socket_bind_in_progress NX_CLEANUP_ARGUMENT);
203
204 /* Release the protection mutex. */
205 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
206
207 /* Return success. */
208 return(NX_SUCCESS);
209 }
210 else
211 {
212
213 /* Release the protection mutex. */
214 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
215
216 /* Return a not bound error code. */
217 return(NX_NOT_BOUND);
218 }
219 }
220
221 /* Otherwise, the socket is bound. We need to remove this socket from the
222 port and check for any other UDP socket bind requests that are queued. */
223
224 /* Pickup the port number in the UDP socket structure. */
225 port = socket_ptr -> nx_udp_socket_port;
226
227 /* Calculate the hash index in the UDP port array of the associated IP instance. */
228 index = (UINT)((port + (port >> 8)) & NX_UDP_PORT_TABLE_MASK);
229
230 #ifdef NX_ENABLE_TCPIP_OFFLOAD
231 _nx_udp_socket_driver_unbind(socket_ptr);
232 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
233
234 /* Disable interrupts while we unlink the current socket. */
235 TX_DISABLE
236
237 /* Determine if this is the only socket bound on this port list. */
238 if (socket_ptr -> nx_udp_socket_bound_next == socket_ptr)
239 {
240
241 /* Yes, this is the only socket on the port list. */
242
243 /* Clear the list head pointer and the next pointer in the socket. */
244 ip_ptr -> nx_ip_udp_port_table[index] = NX_NULL;
245 socket_ptr -> nx_udp_socket_bound_next = NX_NULL;
246 }
247 else
248 {
249
250 /* Relink the neighbors of this UDP socket. */
251
252 /* Update the links of the adjacent sockets. */
253 (socket_ptr -> nx_udp_socket_bound_next) -> nx_udp_socket_bound_previous =
254 socket_ptr -> nx_udp_socket_bound_previous;
255 (socket_ptr -> nx_udp_socket_bound_previous) -> nx_udp_socket_bound_next =
256 socket_ptr -> nx_udp_socket_bound_next;
257
258 /* Determine if the head of the port list points to the socket being removed.
259 If so, we need to move the head pointer. */
260 if (ip_ptr -> nx_ip_udp_port_table[index] == socket_ptr)
261 {
262
263 /* Yes, we need to move the port list head pointer. */
264 ip_ptr -> nx_ip_udp_port_table[index] = socket_ptr -> nx_udp_socket_bound_next;
265 }
266
267 /* Clear the next pointer in the socket to indicate it is no longer bound. */
268 socket_ptr -> nx_udp_socket_bound_next = NX_NULL;
269 }
270
271 /* Restore interrupts. */
272 TX_RESTORE
273
274 /* The socket is off the bound list... we need to check for queued packets and possible
275 receive suspension. We need to clean up either of these conditions. */
276 if (socket_ptr -> nx_udp_socket_receive_count)
277 {
278
279 /* Setup packet pointer. */
280 packet_ptr = socket_ptr -> nx_udp_socket_receive_head;
281
282 /* Clear the head and the tail pointers. */
283 socket_ptr -> nx_udp_socket_receive_head = NX_NULL;
284 socket_ptr -> nx_udp_socket_receive_tail = NX_NULL;
285
286 /* Loop to clear all the packets out. */
287 while (socket_ptr -> nx_udp_socket_receive_count)
288 {
289
290 /* Pickup the next queued packet. */
291 next_packet_ptr = packet_ptr -> nx_packet_queue_next;
292
293 /* Release the packet. */
294 _nx_packet_release(packet_ptr);
295
296 /* Move to the next packet. */
297 packet_ptr = next_packet_ptr;
298
299 /* Decrease the queued packet count. */
300 socket_ptr -> nx_udp_socket_receive_count--;
301 }
302 }
303 else if (socket_ptr -> nx_udp_socket_receive_suspended_count)
304 {
305
306 /* Clear out all threads suspended on this socket. */
307
308 /* Pickup the first suspended thread. */
309 thread_ptr = socket_ptr -> nx_udp_socket_receive_suspension_list;
310
311 /* Clear the thread receive suspension list. */
312 socket_ptr -> nx_udp_socket_receive_suspension_list = NX_NULL;
313
314 /* Walk through the queue list to resume any and all threads suspended
315 on this queue. */
316 while (socket_ptr -> nx_udp_socket_receive_suspended_count)
317 {
318
319 /* Lockout interrupts. */
320 TX_DISABLE
321
322 /* Clear the cleanup pointer, this prevents the timeout from doing
323 anything. */
324 thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
325
326 /* Temporarily disable preemption again. */
327 _tx_thread_preempt_disable++;
328
329 /* Restore interrupts. */
330 TX_RESTORE
331
332 /* Set the return status in the thread to NX_SOCKET_UNBOUND. */
333 thread_ptr -> tx_thread_suspend_status = NX_SOCKET_UNBOUND;
334
335 /* Move the thread pointer ahead. */
336 thread_ptr = thread_ptr -> tx_thread_suspended_next;
337
338 /* Resume the thread. */
339 _tx_thread_system_resume(thread_ptr -> tx_thread_suspended_previous);
340
341 /* Decrease the suspended count. */
342 socket_ptr -> nx_udp_socket_receive_suspended_count--;
343 }
344 }
345
346 /* Disable interrupts again. */
347 TX_DISABLE
348
349 /* Determine if there are any threads suspended on trying to bind to the
350 same port. */
351 thread_ptr = socket_ptr -> nx_udp_socket_bind_suspension_list;
352 if (thread_ptr)
353 {
354
355 /* Remove the suspended thread from the list. */
356
357 /* See if this is the only suspended thread on the list. */
358 if (thread_ptr == thread_ptr -> tx_thread_suspended_next)
359 {
360
361 /* Yes, the only suspended thread. */
362
363 /* Update the head pointer. */
364 socket_ptr -> nx_udp_socket_bind_suspension_list = NX_NULL;
365 }
366 else
367 {
368
369 /* At least one more thread is on the same expiration list. */
370
371 /* Update the list head pointer. */
372 socket_ptr -> nx_udp_socket_bind_suspension_list = thread_ptr -> tx_thread_suspended_next;
373
374 /* Update the links of the adjacent threads. */
375 (thread_ptr -> tx_thread_suspended_next) -> tx_thread_suspended_previous =
376 thread_ptr -> tx_thread_suspended_previous;
377 (thread_ptr -> tx_thread_suspended_previous) -> tx_thread_suspended_next =
378 thread_ptr -> tx_thread_suspended_next;
379 }
380
381 /* Decrement the suspension count. */
382 socket_ptr -> nx_udp_socket_bind_suspended_count--;
383
384 /* Pickup the new socket structure to link to the port list. */
385 new_socket_ptr = (NX_UDP_SOCKET *)thread_ptr -> tx_thread_suspend_control_block;
386
387 /* Clear the new socket's bind in progress flag. */
388 new_socket_ptr -> nx_udp_socket_bind_in_progress = NX_NULL;
389
390 /* Inherit the suspension list from the previously bound socket. */
391 new_socket_ptr -> nx_udp_socket_bind_suspension_list =
392 socket_ptr -> nx_udp_socket_bind_suspension_list;
393 socket_ptr -> nx_udp_socket_bind_suspension_list = NX_NULL;
394
395 /* Link the new socket to the bound list. */
396 if (ip_ptr -> nx_ip_udp_port_table[index])
397 {
398
399 /* There are already sockets on this list... just add this one
400 to the end. */
401 new_socket_ptr -> nx_udp_socket_bound_next =
402 ip_ptr -> nx_ip_udp_port_table[index];
403 new_socket_ptr -> nx_udp_socket_bound_previous =
404 (ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous;
405 ((ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous) -> nx_udp_socket_bound_next =
406 new_socket_ptr;
407 (ip_ptr -> nx_ip_udp_port_table[index]) -> nx_udp_socket_bound_previous = new_socket_ptr;
408 }
409 else
410 {
411
412 /* Nothing is on the UDP port list. Add this UDP socket to an
413 empty list. */
414 new_socket_ptr -> nx_udp_socket_bound_next = new_socket_ptr;
415 new_socket_ptr -> nx_udp_socket_bound_previous = new_socket_ptr;
416 ip_ptr -> nx_ip_udp_port_table[index] = new_socket_ptr;
417 }
418
419 /* Prepare for resumption of the first thread. */
420
421 /* Clear cleanup routine to avoid timeout. */
422 thread_ptr -> tx_thread_suspend_cleanup = TX_NULL;
423
424 /* Temporarily disable preemption. */
425 _tx_thread_preempt_disable++;
426
427 /* Restore interrupts. */
428 TX_RESTORE
429
430 /* Put return status into the thread control block. */
431 thread_ptr -> tx_thread_suspend_status = NX_SUCCESS;
432
433 /* Release the mutex protection. */
434 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
435
436 /* Resume thread. */
437 _tx_thread_system_resume(thread_ptr);
438 }
439 else
440 {
441
442 /* Restore interrupts. */
443 TX_RESTORE
444
445 /* Release the mutex protection. */
446 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
447
448 /* Return success to the caller. */
449 return(NX_SUCCESS);
450 }
451
452 /* Check for preemption. */
453 _tx_thread_system_preempt_check();
454
455 /* Return success. */
456 return(NX_SUCCESS);
457 }
458
459