1 /***************************************************************************
2 * Copyright (c) 2024 Microsoft Corporation
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the MIT License which is available at
6 * https://opensource.org/licenses/MIT.
7 *
8 * SPDX-License-Identifier: MIT
9 **************************************************************************/
10
11
12 /**************************************************************************/
13 /**************************************************************************/
14 /** */
15 /** NetX Component */
16 /** */
17 /** Transmission Control Protocol (TCP) */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22 #define NX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "nx_api.h"
28 #include "nx_ip.h"
29 #ifdef FEATURE_NX_IPV6
30 #include "nx_ipv6.h"
31 #endif /* FEATURE_NX_IPV6 */
32 #include "nx_packet.h"
33 #include "nx_tcp.h"
34 #include "tx_thread.h"
35 #ifdef NX_IPSEC_ENABLE
36 #include "nx_ipsec.h"
37 #endif /* NX_IPSEC_ENABLE */
38
39
40 #ifdef NX_ENABLE_TCPIP_OFFLOAD
41 /**************************************************************************/
42 /* */
43 /* FUNCTION RELEASE */
44 /* */
45 /* _nx_tcp_socket_driver_send PORTABLE C */
46 /* 6.4.0 */
47 /* AUTHOR */
48 /* */
49 /* Yuxin Zhou, Microsoft Corporation */
50 /* */
51 /* DESCRIPTION */
52 /* */
53 /* This function sends a TCP packet through TCP/IP offload interface. */
54 /* */
55 /* INPUT */
56 /* */
57 /* socket_ptr Pointer to socket */
58 /* packet_ptr Pointer to packet to send */
59 /* wait_option Suspension option */
60 /* */
61 /* OUTPUT */
62 /* */
63 /* status Completion status */
64 /* */
65 /* CALLS */
66 /* */
67 /* _nx_ip_packet_send Packet send function */
68 /* _nx_ipv6_packet_send Packet send function */
69 /* */
70 /* CALLED BY */
71 /* */
72 /* _nx_tcp_socket_send_internal */
73 /* */
74 /* RELEASE HISTORY */
75 /* */
76 /* DATE NAME DESCRIPTION */
77 /* */
78 /* 08-02-2021 Yuxin Zhou Initial Version 6.1.8 */
79 /* 12-31-2023 Yajun Xia Modified comment(s), */
80 /* supported VLAN, */
81 /* resulting in version 6.4.0 */
82 /* */
83 /**************************************************************************/
_nx_tcp_socket_driver_send(NX_TCP_SOCKET * socket_ptr,NX_PACKET * packet_ptr,ULONG wait_option)84 static UINT _nx_tcp_socket_driver_send(NX_TCP_SOCKET *socket_ptr, NX_PACKET *packet_ptr, ULONG wait_option)
85 {
86 UINT status;
87 NX_IP *ip_ptr;
88 NX_INTERFACE *interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
89 #ifdef NX_ENABLE_IP_PACKET_FILTER
90 UCHAR *original_ptr = packet_ptr -> nx_packet_prepend_ptr;
91 ULONG original_length = packet_ptr -> nx_packet_length;
92 NX_TCP_HEADER *header_ptr;
93 #endif /* NX_ENABLE_IP_PACKET_FILTER */
94
95 /* Setup the pointer to the associated IP instance. */
96 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
97
98 #ifdef NX_ENABLE_IP_PACKET_FILTER
99 /* Check if the IP packet filter is set. */
100 if (ip_ptr -> nx_ip_packet_filter || ip_ptr -> nx_ip_packet_filter_extended)
101 {
102
103 /* Yes, add the TCP and IP Header to trigger filtering. */
104 /* Prepend the TCP header to the packet. First, make room for the TCP header. */
105 packet_ptr -> nx_packet_prepend_ptr = packet_ptr -> nx_packet_prepend_ptr - sizeof(NX_TCP_HEADER);
106
107 /* Add the length of the TCP header. */
108 packet_ptr -> nx_packet_length = packet_ptr -> nx_packet_length + (ULONG)sizeof(NX_TCP_HEADER);
109
110 /* Pickup the pointer to the head of the TCP packet. */
111 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
112 header_ptr = (NX_TCP_HEADER *)packet_ptr -> nx_packet_prepend_ptr;
113
114 /* Build the output request in the TCP header. */
115 header_ptr -> nx_tcp_header_word_0 = (((ULONG)(socket_ptr -> nx_tcp_socket_port)) << NX_SHIFT_BY_16) |
116 (ULONG)socket_ptr -> nx_tcp_socket_connect_port;
117 header_ptr -> nx_tcp_acknowledgment_number = 0;
118 header_ptr -> nx_tcp_sequence_number = 0;
119 header_ptr -> nx_tcp_header_word_3 = NX_TCP_HEADER_SIZE | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT;
120 header_ptr -> nx_tcp_header_word_4 = 0;
121
122 /* Endian swapping logic. If NX_LITTLE_ENDIAN is specified, these macros will
123 swap the endian of the TCP header. */
124 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_0);
125 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_3);
126
127 #ifndef NX_DISABLE_IPV4
128 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
129 {
130 _nx_ip_header_add(ip_ptr, packet_ptr,
131 socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_ip_address,
132 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4,
133 socket_ptr -> nx_tcp_socket_type_of_service,
134 socket_ptr -> nx_tcp_socket_time_to_live,
135 NX_IP_TCP,
136 socket_ptr -> nx_tcp_socket_fragment_enable);
137 }
138 #endif /* !NX_DISABLE_IPV4 */
139
140 #ifdef FEATURE_NX_IPV6
141 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
142 {
143 if (_nx_ipv6_header_add(ip_ptr, &packet_ptr,
144 NX_PROTOCOL_TCP,
145 packet_ptr -> nx_packet_length,
146 ip_ptr -> nx_ipv6_hop_limit,
147 socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address,
148 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6,
149 NX_NULL))
150 {
151
152 /* Invalid interface address. Just return success. */
153 return(NX_SUCCESS);
154 }
155 }
156 #endif /* FEATURE_NX_IPV6 */
157
158 if (ip_ptr -> nx_ip_packet_filter)
159 {
160 if (ip_ptr -> nx_ip_packet_filter((VOID *)(packet_ptr -> nx_packet_prepend_ptr),
161 NX_IP_PACKET_OUT) != NX_SUCCESS)
162 {
163
164 /* Packet consumed by IP filter. Just return success. */
165 _nx_packet_transmit_release(packet_ptr);
166 return(NX_SUCCESS);
167 }
168 }
169
170 /* Check if the IP packet filter extended is set. */
171 if (ip_ptr -> nx_ip_packet_filter_extended)
172 {
173
174 /* Yes, call the IP packet filter extended routine. */
175 if (ip_ptr -> nx_ip_packet_filter_extended(ip_ptr, packet_ptr, NX_IP_PACKET_OUT) != NX_SUCCESS)
176 {
177
178 /* Packet consumed by IP filter. Just return success. */
179 _nx_packet_transmit_release(packet_ptr);
180 return(NX_SUCCESS);
181 }
182 }
183
184 /* Reset UDP and IP header. */
185 packet_ptr -> nx_packet_prepend_ptr = original_ptr;
186 packet_ptr -> nx_packet_length = original_length;
187 }
188 #endif /* NX_ENABLE_IP_PACKET_FILTER */
189
190 /* Let TCP/IP offload interface send the packet. */
191 status = interface_ptr -> nx_interface_tcpip_offload_handler(ip_ptr, interface_ptr, socket_ptr,
192 NX_TCPIP_OFFLOAD_TCP_SOCKET_SEND,
193 packet_ptr, NX_NULL, NX_NULL, 0, NX_NULL,
194 wait_option);
195
196 if (status)
197 {
198 return(NX_TCPIP_OFFLOAD_ERROR);
199 }
200 else
201 {
202 return(NX_SUCCESS);
203 }
204 }
205 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
206
207 /**************************************************************************/
208 /* */
209 /* FUNCTION RELEASE */
210 /* */
211 /* _nx_tcp_socket_send_internal PORTABLE C */
212 /* 6.1.10 */
213 /* AUTHOR */
214 /* */
215 /* Yuxin Zhou, Microsoft Corporation */
216 /* */
217 /* DESCRIPTION */
218 /* */
219 /* This function sends a TCP packet through the specified socket. */
220 /* */
221 /* INPUT */
222 /* */
223 /* socket_ptr Pointer to socket */
224 /* packet_ptr Pointer to packet to send */
225 /* wait_option Suspension option */
226 /* */
227 /* OUTPUT */
228 /* */
229 /* status Completion status */
230 /* */
231 /* CALLS */
232 /* */
233 /* _nx_ip_packet_send Packet send function */
234 /* _nx_ipv6_packet_send Packet send function */
235 /* _nx_ip_checksum_compute Calculate TCP checksum */
236 /* _nx_tcp_socket_thread_suspend Suspend calling thread */
237 /* tx_mutex_get Get protection mutex */
238 /* tx_mutex_put Put protection mutex */
239 /* _nx_tcp_socket_driver_send TCP/IP offload send function */
240 /* */
241 /* CALLED BY */
242 /* */
243 /* _nx_tcp_socket_send */
244 /* */
245 /* NOTE: */
246 /* */
247 /* This is an internal function, only being called by */
248 /* _nx_tcp_socket_send(). The caller guarantees that the payload */
249 /* size does not exceed MSS. */
250 /* */
251 /* RELEASE HISTORY */
252 /* */
253 /* DATE NAME DESCRIPTION */
254 /* */
255 /* 05-19-2020 Yuxin Zhou Initial Version 6.0 */
256 /* 09-30-2020 Yuxin Zhou Modified comment(s), */
257 /* resulting in version 6.1 */
258 /* 08-02-2021 Yuxin Zhou Modified comment(s), and */
259 /* supported TCP/IP offload, */
260 /* resulting in version 6.1.8 */
261 /* 10-15-2021 Yuxin Zhou Modified comment(s), and */
262 /* fixed the bug of race */
263 /* condition, */
264 /* resulting in version 6.1.9 */
265 /* 01-31-2022 Yuxin Zhou Modified comment(s), and */
266 /* improved the throughput of */
267 /* TCP transmission, */
268 /* resulting in version 6.1.10 */
269 /* */
270 /**************************************************************************/
_nx_tcp_socket_send_internal(NX_TCP_SOCKET * socket_ptr,NX_PACKET * packet_ptr,ULONG wait_option)271 UINT _nx_tcp_socket_send_internal(NX_TCP_SOCKET *socket_ptr, NX_PACKET *packet_ptr, ULONG wait_option)
272 {
273
274 TX_INTERRUPT_SAVE_AREA
275
276 NX_IP *ip_ptr;
277 NX_PACKET_POOL *pool_ptr;
278 NX_TCP_HEADER *header_ptr;
279 ULONG checksum = 0;
280 ULONG sequence_number;
281 ULONG tx_window_current;
282 ULONG remaining_bytes;
283 ULONG *source_ip = NX_NULL, *dest_ip = NX_NULL;
284 ULONG send_mss;
285 NX_PACKET *send_packet = packet_ptr;
286 NX_PACKET *current_packet;
287 UCHAR *current_ptr;
288 ULONG data_offset = 0;
289 ULONG source_data_size;
290 ULONG copy_size;
291 UINT data_left;
292 UINT ret;
293 UCHAR preempted = NX_FALSE;
294 UCHAR adjust_packet;
295 UINT old_threshold = 0;
296 ULONG window_size;
297 #ifdef NX_ENABLE_TCPIP_OFFLOAD
298 UINT status;
299 NX_INTERFACE *interface_ptr;
300 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
301 #if defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
302 UINT compute_checksum = 1;
303 #endif /* defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
304
305 #ifdef NX_DISABLE_TCP_TX_CHECKSUM
306 compute_checksum = 0;
307 #endif /* NX_DISABLE_TCP_TX_CHECKSUM */
308
309 /* Check packet length. */
310 if (packet_ptr -> nx_packet_length == 0)
311 {
312
313 /* Empty packet is not allowed. */
314 return(NX_INVALID_PACKET);
315 }
316
317 /* Lockout interrupts. */
318 TX_DISABLE
319
320 /* Determine if the socket is currently bound. */
321 if (!socket_ptr -> nx_tcp_socket_bound_next)
322 {
323
324 /* Restore interrupts. */
325 TX_RESTORE
326
327 /* Socket is not bound, return an error message. */
328 return(NX_NOT_BOUND);
329 }
330
331 /* Pickup the important information from the socket. */
332
333 /* Setup the pointer to the associated IP instance. */
334 ip_ptr = socket_ptr -> nx_tcp_socket_ip_ptr;
335
336 /* Restore interrupts. */
337 TX_RESTORE
338
339 /* Check if the connection is in progress. */
340 if ((socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_SENT) || (socket_ptr -> nx_tcp_socket_state == NX_TCP_SYN_RECEIVED))
341 {
342
343 /* Yes it it. Wait for establish state. */
344 _nx_tcp_socket_state_wait(socket_ptr, NX_TCP_ESTABLISHED, wait_option);
345 }
346
347 /* Obtain the IP mutex. */
348 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
349
350 /* Check for the socket being in an established state. */
351 if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_ESTABLISHED) && (socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSE_WAIT))
352 {
353
354 /* Release the protection. */
355 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
356
357 /* Socket is not connected, return an error message. */
358 return(NX_NOT_CONNECTED);
359 }
360
361 /* Add debug information. */
362 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
363
364 #ifndef NX_DISABLE_IPV4
365 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
366 {
367
368 /* Set the source address. */
369 source_ip = &socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_ip_address;
370
371 /* Set the destination address. */
372 dest_ip = &socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4;
373
374 /* The outgoing interface should have been stored in the socket structure. */
375 packet_ptr -> nx_packet_address.nx_packet_interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
376
377 /* Calculate the data offset required by fragmented TCP packet. */
378 data_offset = NX_PHYSICAL_HEADER + sizeof(NX_IPV4_HEADER) + sizeof(NX_TCP_HEADER);
379 }
380 #endif /* !NX_DISABLE_IPV4 */
381
382 #ifdef FEATURE_NX_IPV6
383 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
384 {
385
386 /* Determine whether or not the IPv6 address is valid. */
387 if (socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address_state != NX_IPV6_ADDR_STATE_VALID)
388 {
389
390 /* Release the protection. */
391 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
392
393 /* Add debug information. */
394 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
395
396 return(NX_NO_INTERFACE_ADDRESS);
397 }
398 /* Set the source address. */
399 source_ip = socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address;
400
401 /* Set the destination address. */
402 dest_ip = socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6;
403
404 /* The outgoing address should have been stored in the socket structure. */
405 packet_ptr -> nx_packet_address.nx_packet_ipv6_address_ptr = socket_ptr -> nx_tcp_socket_ipv6_addr;
406
407 /* Calculate the data offset required by fragmented TCP packet. */
408 data_offset = NX_PHYSICAL_HEADER + sizeof(NX_IPV6_HEADER) + sizeof(NX_TCP_HEADER);
409 }
410 #endif /* FEATURE_NX_IPV6 */
411
412 #ifdef NX_ENABLE_TCPIP_OFFLOAD
413 interface_ptr = socket_ptr -> nx_tcp_socket_connect_interface;
414 if ((interface_ptr -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCPIP_OFFLOAD) &&
415 (interface_ptr -> nx_interface_tcpip_offload_handler))
416 {
417
418 /* This interface supports TCP/IP offload. */
419 status = _nx_tcp_socket_driver_send(socket_ptr, packet_ptr, wait_option);
420
421 /* Release the IP protection. */
422 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
423
424 return(status);
425 }
426 #endif /* NX_ENABLE_TCPIP_OFFLOAD */
427
428 #ifdef NX_IPSEC_ENABLE
429 /* Increase the data offset when IPsec is enabled. */
430 data_offset += socket_ptr -> nx_tcp_socket_egress_sa_data_offset;
431 #endif /* NX_IPSEC_ENABLE */
432
433 /* If trace is enabled, insert this event into the trace buffer. */
434 NX_TRACE_IN_LINE_INSERT(NX_TRACE_TCP_SOCKET_SEND, socket_ptr, packet_ptr, packet_ptr -> nx_packet_length, socket_ptr -> nx_tcp_socket_tx_sequence, NX_TRACE_TCP_EVENTS, 0, 0);
435
436 /* Get the max mss this socket could send */
437 send_mss = socket_ptr -> nx_tcp_socket_connect_mss;
438
439 /* Get original pool. */
440 pool_ptr = packet_ptr -> nx_packet_pool_owner;
441
442 /* Loop to send the packet. */
443 for (;;)
444 {
445
446 /* Pick up the min(cwnd, swnd) */
447 if (socket_ptr -> nx_tcp_socket_tx_window_advertised > socket_ptr -> nx_tcp_socket_tx_window_congestion)
448 {
449 tx_window_current = socket_ptr -> nx_tcp_socket_tx_window_congestion;
450
451 /* On the first and second duplicate ACKs received, the total FlightSize would
452 remain less than or equal to cwnd plus 2*SMSS.
453 Section 3.2, Page 9, RFC5681. */
454 if ((socket_ptr -> nx_tcp_socket_duplicated_ack_received == 1) ||
455 (socket_ptr -> nx_tcp_socket_duplicated_ack_received == 2))
456 {
457 tx_window_current += (socket_ptr -> nx_tcp_socket_connect_mss << 1);
458
459 /* Make sure the tx_window_current is less or equal to swnd. */
460 if (tx_window_current > socket_ptr -> nx_tcp_socket_tx_window_advertised)
461 {
462 tx_window_current = socket_ptr -> nx_tcp_socket_tx_window_advertised;
463 }
464 }
465 }
466 else
467 {
468 tx_window_current = socket_ptr -> nx_tcp_socket_tx_window_advertised;
469 }
470
471 /* Substract any data transmitted but unacked (outstanding bytes) */
472 if (tx_window_current > socket_ptr -> nx_tcp_socket_tx_outstanding_bytes)
473 {
474 tx_window_current -= socket_ptr -> nx_tcp_socket_tx_outstanding_bytes;
475 }
476 else /* Set tx_window_current to zero. */
477 {
478 tx_window_current = 0;
479 }
480
481 /* Pick up the min(tx_window, send_mss). */
482 if (tx_window_current > send_mss)
483 {
484 tx_window_current = send_mss;
485 }
486
487
488 /* Store the data that is left. */
489 data_left = packet_ptr -> nx_packet_length;
490
491 /* Check whether data can be sent. */
492 if ((tx_window_current != 0) && (socket_ptr -> nx_tcp_socket_transmit_sent_count < socket_ptr -> nx_tcp_socket_transmit_queue_maximum))
493 {
494
495 /* Whether to adjust the packet? */
496 if (packet_ptr -> nx_packet_length > tx_window_current)
497 {
498
499 /* Packet need to be fragmented. */
500 adjust_packet = NX_TRUE;
501 }
502 /*lint -e(923) suppress cast of pointer to ULONG. */
503 else if (((ALIGN_TYPE)packet_ptr -> nx_packet_prepend_ptr) & 3)
504 {
505
506 /* Starting address of TCP header need to be four bytes aligned. */
507 adjust_packet = NX_TRUE;
508 }
509 #ifndef NX_DISABLE_PACKET_CHAIN
510 else if ((packet_ptr -> nx_packet_next != NX_NULL) &&
511 ((packet_ptr -> nx_packet_length + data_offset) < pool_ptr -> nx_packet_pool_payload_size) &&
512 (pool_ptr -> nx_packet_pool_available > 0))
513 {
514
515 /* All data can be sent in one packet but they are in chained packets. */
516 adjust_packet = NX_TRUE;
517 }
518 else if (packet_ptr -> nx_packet_prepend_ptr == packet_ptr -> nx_packet_append_ptr)
519 {
520
521 /* Loop to find the first byte of data. */
522 current_packet = packet_ptr -> nx_packet_next;
523
524 while ((current_packet != NX_NULL) && (current_packet -> nx_packet_prepend_ptr == current_packet -> nx_packet_append_ptr))
525 {
526
527 /* Move to next packet. */
528 current_packet = current_packet -> nx_packet_next;
529 }
530
531 /* packet length is not 0. Therefore the packet chain is expected to contain data. */
532 NX_ASSERT(current_packet != NX_NULL);
533
534 /*lint -e{923} suppress cast of pointer to ULONG. */
535 if (((ALIGN_TYPE)current_packet -> nx_packet_prepend_ptr) & 3)
536 {
537
538 /* Starting address of TCP data need to be four bytes aligned. */
539 adjust_packet = NX_TRUE;
540 }
541 else
542 {
543
544 /* Packet can be sent directly. */
545 adjust_packet = NX_FALSE;
546 }
547 }
548 #endif /* NX_DISABLE_PACKET_CHAIN */
549 else
550 {
551
552 /* Packet can be sent directly. */
553 adjust_packet = NX_FALSE;
554 }
555
556 /* Adjust the packet? */
557 if (adjust_packet)
558 {
559
560 /* Yes. Obtain the size of the packet can be sent. */
561 if (packet_ptr -> nx_packet_length > tx_window_current)
562 {
563 remaining_bytes = tx_window_current;
564 }
565 else
566 {
567 remaining_bytes = packet_ptr -> nx_packet_length;
568 }
569
570 /* Points to the source packet. */
571 current_packet = packet_ptr;
572
573 /* Mark the beginning of data. */
574 current_ptr = packet_ptr -> nx_packet_prepend_ptr;
575
576 /* Release the protection. */
577 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
578
579 /* Obtain a new segmentation. */
580 ret = _nx_packet_allocate(pool_ptr, &send_packet,
581 data_offset, wait_option);
582
583 if (ret != NX_SUCCESS)
584 {
585
586 /* Restore preemption? */
587 if (preempted == NX_TRUE)
588 {
589
590 /*lint -e{644} -e{530} suppress variable might not be initialized, since "old_threshold" was initialized when preempted was set to NX_TRUE. */
591 tx_thread_preemption_change(_tx_thread_current_ptr, old_threshold, &old_threshold);
592 }
593
594 /* Packet allocate failure. Return.*/
595 return(ret);
596 }
597
598 /* Regain exclusive access to IP instance. */
599 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
600
601 /* Add debug information. */
602 NX_PACKET_DEBUG(__FILE__, __LINE__, send_packet);
603
604 /* Loop through the entire source packet. */
605 while (remaining_bytes)
606 {
607
608 /* Figure out whether or not the source packet still contains data. */
609 /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
610 source_data_size = (ULONG)(current_packet -> nx_packet_append_ptr - current_ptr);
611 while (source_data_size == 0)
612 {
613
614 #ifndef NX_DISABLE_PACKET_CHAIN
615 /* The current buffer is exhausted. Move to the next buffer on the source packet chain. */
616 current_packet = current_packet -> nx_packet_next;
617
618 if (current_packet == NX_NULL)
619 {
620 #endif /* NX_DISABLE_PACKET_CHAIN */
621
622 /* Restore preemption? */
623 if (preempted == NX_TRUE)
624 {
625 tx_thread_preemption_change(_tx_thread_current_ptr, old_threshold, &old_threshold);
626 }
627
628 /* No more data in the source packet. However there are still bytes remaining even though
629 the packet is not done yet. This is an unrecoverable error. */
630 /*lint -e{644} suppress variable might not be initialized, since "send_packet" was initialized in _nx_packet_allocate. */
631 _nx_packet_release(send_packet);
632
633 /* Release the protection. */
634 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
635
636 /* Add debug information. */
637 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
638
639 return(NX_INVALID_PACKET);
640 #ifndef NX_DISABLE_PACKET_CHAIN
641 }
642
643 /* Mark the beginning of data in the next packet. */
644 current_ptr = current_packet -> nx_packet_prepend_ptr;
645
646 /* Compute the amount of data present in this source buffer. */
647 /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
648 source_data_size = (ULONG)(current_packet -> nx_packet_append_ptr - current_ptr);
649 #endif /* NX_DISABLE_PACKET_CHAIN */
650 }
651
652
653 /* copy_size = min(send_packet, source) */
654 if (remaining_bytes > source_data_size)
655 {
656 copy_size = source_data_size;
657 }
658 else
659 {
660 copy_size = remaining_bytes;
661 }
662
663 /* Release the mutex before a blocking call. */
664 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
665
666 /* Append data. */
667 ret = _nx_packet_data_append(send_packet, current_ptr, copy_size,
668 pool_ptr, wait_option);
669
670 /* Regain exclusive access to IP instance. */
671 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
672
673 /* Check for errors with data append. */
674 if (ret != NX_SUCCESS)
675 {
676
677 /* Append failed. */
678 if (send_packet -> nx_packet_length == 0)
679 {
680
681 /* The packet is empty, return. */
682 /* Restore preemption? */
683 if (preempted == NX_TRUE)
684 {
685
686 /*lint -e{644} -e{530} suppress variable might not be initialized, since "old_threshold" was initialized when preempted was set to NX_TRUE. */
687 tx_thread_preemption_change(_tx_thread_current_ptr, old_threshold, &old_threshold);
688 }
689
690 /* Release the protection. */
691 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
692
693 /* Release the packet. */
694 _nx_packet_release(send_packet);
695
696 /* Packet allocate failure. Return.*/
697 return(ret);
698 }
699
700 /* Partial data can be sent. Just break. */
701 break;
702 }
703
704 /* Reduce the remaining_bytes counter by the amount being copied over. */
705 remaining_bytes -= copy_size;
706
707 /* Advance the prepend ptr on the source buffer, by the amount being copied. */
708 current_ptr += copy_size;
709 }
710
711 send_packet -> nx_packet_address = packet_ptr -> nx_packet_address;
712 }
713 else
714 {
715
716 /* Send the packet directly. */
717 send_packet = packet_ptr;
718 }
719
720 /* Now the send_packet can be sent. */
721 /* Set IP version. */
722 send_packet -> nx_packet_ip_version = (UCHAR)(socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version);
723
724 #ifdef NX_IPSEC_ENABLE
725 send_packet -> nx_packet_ipsec_sa_ptr = socket_ptr -> nx_tcp_socket_egress_sa;
726 #endif /* NX_IPSEC_ENABLE */
727
728 /* Prepend the TCP header to the packet. First, make room for the TCP header. */
729 send_packet -> nx_packet_prepend_ptr = send_packet -> nx_packet_prepend_ptr - sizeof(NX_TCP_HEADER);
730
731 /* Add the length of the TCP header. */
732 send_packet -> nx_packet_length = send_packet -> nx_packet_length + (ULONG)sizeof(NX_TCP_HEADER);
733
734 /* Pickup the pointer to the head of the TCP packet. */
735 /*lint -e{927} -e{826} suppress cast of pointer to pointer, since it is necessary */
736 header_ptr = (NX_TCP_HEADER *)send_packet -> nx_packet_prepend_ptr;
737
738 /* Build the output request in the TCP header. */
739 header_ptr -> nx_tcp_header_word_0 = (((ULONG)(socket_ptr -> nx_tcp_socket_port)) << NX_SHIFT_BY_16) | (ULONG)socket_ptr -> nx_tcp_socket_connect_port;
740 header_ptr -> nx_tcp_acknowledgment_number = socket_ptr -> nx_tcp_socket_rx_sequence;
741
742 /* Set window size. */
743 #ifdef NX_ENABLE_TCP_WINDOW_SCALING
744 window_size = socket_ptr -> nx_tcp_socket_rx_window_current >> socket_ptr -> nx_tcp_rcv_win_scale_value;
745
746 /* Make sure the window_size is less than 0xFFFF. */
747 if (window_size > 0xFFFF)
748 {
749 window_size = 0xFFFF;
750 }
751 #else
752 window_size = socket_ptr -> nx_tcp_socket_rx_window_current;
753 #endif /* NX_ENABLE_TCP_WINDOW_SCALING */
754
755 header_ptr -> nx_tcp_header_word_3 = NX_TCP_HEADER_SIZE | NX_TCP_ACK_BIT | NX_TCP_PSH_BIT | window_size;
756 header_ptr -> nx_tcp_header_word_4 = 0;
757
758 /* Remember the last ACKed sequence and the last reported window size. */
759 socket_ptr -> nx_tcp_socket_rx_sequence_acked = socket_ptr -> nx_tcp_socket_rx_sequence;
760 socket_ptr -> nx_tcp_socket_rx_window_last_sent = socket_ptr -> nx_tcp_socket_rx_window_current;
761
762 /* Setup a new delayed ACK timeout. */
763 socket_ptr -> nx_tcp_socket_delayed_ack_timeout = _nx_tcp_ack_timer_rate;
764
765 /* Endian swapping logic. If NX_LITTLE_ENDIAN is specified, these macros will
766 swap the endian of the TCP header. */
767 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_0);
768 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_acknowledgment_number);
769 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_3);
770 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
771
772 /* Release the protection. */
773 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
774
775 /* Pickup the current transmit sequence number. */
776 header_ptr -> nx_tcp_sequence_number = socket_ptr -> nx_tcp_socket_tx_sequence;
777 sequence_number = header_ptr -> nx_tcp_sequence_number;
778
779 /* Swap the headers for endianness. */
780 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_sequence_number);
781
782 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
783 if (socket_ptr -> nx_tcp_socket_connect_interface -> nx_interface_capability_flag & NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM)
784 {
785 compute_checksum = 0;
786 }
787 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
788
789 #ifdef NX_IPSEC_ENABLE
790 if ((send_packet -> nx_packet_ipsec_sa_ptr != NX_NULL) && (((NX_IPSEC_SA *)(send_packet -> nx_packet_ipsec_sa_ptr)) -> nx_ipsec_sa_encryption_method != NX_CRYPTO_NONE))
791 {
792 compute_checksum = 1;
793 }
794 #endif /* NX_IPSEC_ENABLE */
795
796 #if defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE)
797 if (compute_checksum)
798 #endif /* defined(NX_DISABLE_TCP_TX_CHECKSUM) || defined(NX_ENABLE_INTERFACE_CAPABILITY) || defined(NX_IPSEC_ENABLE) */
799 {
800 /* Calculate the TCP checksum without protection. */
801 checksum = _nx_ip_checksum_compute(send_packet, NX_PROTOCOL_TCP,
802 (UINT)send_packet -> nx_packet_length,
803 source_ip, dest_ip);
804 checksum = ~checksum & NX_LOWER_16_MASK;
805 }
806 #ifdef NX_ENABLE_INTERFACE_CAPABILITY
807 else
808 {
809 send_packet -> nx_packet_interface_capability_flag |= NX_INTERFACE_CAPABILITY_TCP_TX_CHECKSUM;
810 }
811 #endif /* NX_ENABLE_INTERFACE_CAPABILITY */
812
813 /* Place protection while we check the sequence number for the new TCP packet. */
814 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
815
816 /* Determine if the sequence number is the same. */
817 if (sequence_number != socket_ptr -> nx_tcp_socket_tx_sequence)
818 {
819
820 /* Another transmit on this socket took place and changed the sequence. We need to
821 recalculate the checksum with a new sequence number. Release protection and
822 just resume the loop. */
823 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
824
825 /* Release the packet when the sequence is changed. */
826 if (send_packet != packet_ptr)
827 {
828 _nx_packet_release(send_packet);
829 }
830
831 /* Regain exclusive access to IP instance. */
832 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
833 continue;
834 }
835
836 /* Check for the socket being in an established state. It's possible the connection could have gone
837 away during the TCP checksum calculation above. */
838 if ((socket_ptr -> nx_tcp_socket_state != NX_TCP_ESTABLISHED) && (socket_ptr -> nx_tcp_socket_state != NX_TCP_CLOSE_WAIT))
839 {
840
841 /* Restore preemption? */
842 if (preempted == NX_TRUE)
843 {
844 tx_thread_preemption_change(_tx_thread_current_ptr, old_threshold, &old_threshold);
845 }
846
847 /* Release protection. */
848 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
849
850 /* Release the packet when the sequence is changed. */
851 if (send_packet != packet_ptr)
852 {
853 _nx_packet_release(send_packet);
854 }
855
856 /* Add debug information. */
857 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
858
859 /* Socket is not connected, return an error message. */
860 return(NX_NOT_CONNECTED);
861 }
862
863 /* Disable interrupts. */
864 TX_DISABLE
865
866 /* Adjust the transmit sequence number to reflect the output data. */
867 socket_ptr -> nx_tcp_socket_tx_sequence = socket_ptr -> nx_tcp_socket_tx_sequence +
868 (send_packet -> nx_packet_length - (ULONG)sizeof(NX_TCP_HEADER));
869
870 /* Restore interrupts. */
871 TX_RESTORE
872
873 /* Reset zero window probe flag. */
874 socket_ptr -> nx_tcp_socket_zero_window_probe_has_data = NX_FALSE;
875
876 /* Move the checksum into header. */
877 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
878 header_ptr -> nx_tcp_header_word_4 = (checksum << NX_SHIFT_BY_16);
879 NX_CHANGE_ULONG_ENDIAN(header_ptr -> nx_tcp_header_word_4);
880
881 /* Place the packet on the sent list. */
882 data_left -= (send_packet -> nx_packet_length - (ULONG)sizeof(NX_TCP_HEADER));
883 if (socket_ptr -> nx_tcp_socket_transmit_sent_head)
884 {
885
886 /* Yes, other packets are on the list already. Just add this one to the tail. */
887 (socket_ptr -> nx_tcp_socket_transmit_sent_tail) -> nx_packet_union_next.nx_packet_tcp_queue_next = send_packet;
888 socket_ptr -> nx_tcp_socket_transmit_sent_tail = send_packet;
889 }
890 else
891 {
892
893 /* Empty list, just setup the head and tail to the current packet. */
894 socket_ptr -> nx_tcp_socket_transmit_sent_head = send_packet;
895 socket_ptr -> nx_tcp_socket_transmit_sent_tail = send_packet;
896
897 /* Setup a timeout for the packet at the head of the list. */
898 socket_ptr -> nx_tcp_socket_timeout = socket_ptr -> nx_tcp_socket_timeout_rate;
899 socket_ptr -> nx_tcp_socket_timeout_retries = 0;
900 socket_ptr -> nx_tcp_socket_tx_outstanding_bytes = 0;
901 }
902
903 /* Set the next pointer to NX_PACKET_ENQUEUED to indicate the packet is part of a TCP queue. */
904 /*lint -e{923} suppress cast of ULONG to pointer. */
905 send_packet -> nx_packet_union_next.nx_packet_tcp_queue_next = (NX_PACKET *)NX_PACKET_ENQUEUED;
906
907 /* Increment the packet sent count. */
908 socket_ptr -> nx_tcp_socket_transmit_sent_count++;
909
910 /* Increase the transmit outstanding byte count. */
911 socket_ptr -> nx_tcp_socket_tx_outstanding_bytes +=
912 (send_packet -> nx_packet_length - (ULONG)sizeof(NX_TCP_HEADER));
913 #ifndef NX_DISABLE_TCP_INFO
914 /* Increment the TCP packet sent count and bytes sent count. */
915 ip_ptr -> nx_ip_tcp_packets_sent++;
916 ip_ptr -> nx_ip_tcp_bytes_sent += send_packet -> nx_packet_length - (ULONG)sizeof(NX_TCP_HEADER);
917
918 /* Increment the TCP packet sent count and bytes sent count for the socket. */
919 socket_ptr -> nx_tcp_socket_packets_sent++;
920 socket_ptr -> nx_tcp_socket_bytes_sent += send_packet -> nx_packet_length - (ULONG)sizeof(NX_TCP_HEADER);
921 #endif /* NX_DISABLE_TCP_INFO */
922
923 #ifdef NX_ENABLE_VLAN
924 if (socket_ptr -> nx_tcp_socket_vlan_priority != NX_VLAN_PRIORITY_INVALID)
925 {
926 send_packet -> nx_packet_vlan_priority = socket_ptr -> nx_tcp_socket_vlan_priority;
927 }
928 #endif /* NX_ENABLE_VLAN */
929
930 /* If trace is enabled, insert this event into the trace buffer. */
931 NX_TRACE_IN_LINE_INSERT(NX_TRACE_INTERNAL_TCP_DATA_SEND, ip_ptr, socket_ptr, send_packet, socket_ptr -> nx_tcp_socket_tx_sequence - (send_packet -> nx_packet_length - sizeof(NX_TCP_HEADER)), NX_TRACE_INTERNAL_EVENTS, 0, 0);
932
933 /* Send the TCP packet to the IP component. */
934 #ifndef NX_DISABLE_IPV4
935 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V4)
936 {
937
938
939 _nx_ip_packet_send(ip_ptr, send_packet,
940 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v4,
941 socket_ptr -> nx_tcp_socket_type_of_service,
942 socket_ptr -> nx_tcp_socket_time_to_live,
943 NX_IP_TCP,
944 socket_ptr -> nx_tcp_socket_fragment_enable,
945 socket_ptr -> nx_tcp_socket_next_hop_address);
946 }
947 #endif /* !NX_DISABLE_IPV4 */
948
949 #ifdef FEATURE_NX_IPV6
950 if (socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_version == NX_IP_VERSION_V6)
951 {
952
953 /* Ready to send the packet! */
954 _nx_ipv6_packet_send(ip_ptr,
955 send_packet,
956 NX_PROTOCOL_TCP,
957 send_packet -> nx_packet_length,
958 ip_ptr -> nx_ipv6_hop_limit,
959 socket_ptr -> nx_tcp_socket_ipv6_addr -> nxd_ipv6_address,
960 socket_ptr -> nx_tcp_socket_connect_ip.nxd_ip_address.v6);
961 }
962 #endif /* FEATURE_NX_IPV6 */
963
964 if (data_left == 0)
965 {
966
967 /* Release the packet. */
968 if (send_packet != packet_ptr)
969 {
970 _nx_packet_release(packet_ptr);
971 }
972
973 /* Restore preemption? */
974 if (preempted == NX_TRUE)
975 {
976 tx_thread_preemption_change(_tx_thread_current_ptr, old_threshold, &old_threshold);
977 }
978
979 /* Release the protection. */
980 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
981
982 /* Add debug information. */
983 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
984
985 /* Return successful status. */
986 return(NX_SUCCESS);
987 }
988 else
989 {
990
991 /* Adjust the orginal packet. */
992 current_packet = packet_ptr;
993
994 remaining_bytes = packet_ptr -> nx_packet_length - data_left;
995 #ifndef NX_DISABLE_PACKET_CHAIN
996 /*lint -e{946} -e{947} suppress pointer subtraction, since it is necessary. */
997 while (remaining_bytes >
998 (UINT)(current_packet -> nx_packet_append_ptr - current_packet -> nx_packet_prepend_ptr))
999 {
1000
1001 /* Trim all data in the train. */
1002 /*lint -e{923} suppress cast of pointer to ULONG. */
1003 packet_ptr -> nx_packet_length -= (ULONG)((ALIGN_TYPE)current_packet -> nx_packet_append_ptr - (ALIGN_TYPE)current_packet -> nx_packet_prepend_ptr);
1004
1005 /*lint -e{923} suppress cast of pointer to ULONG. */
1006 remaining_bytes -= (ULONG)((ALIGN_TYPE)current_packet -> nx_packet_append_ptr - (ALIGN_TYPE)current_packet -> nx_packet_prepend_ptr);
1007
1008 /*lint -e{923} suppress cast between ULONG and pointer. */
1009 current_packet -> nx_packet_append_ptr = (UCHAR *)(((ALIGN_TYPE)current_packet -> nx_packet_append_ptr) & (ALIGN_TYPE)(~3));
1010 current_packet -> nx_packet_prepend_ptr = current_packet -> nx_packet_append_ptr;
1011
1012 /* Pointer to next packet. */
1013 current_packet = current_packet -> nx_packet_next;
1014 }
1015 #endif /* NX_DISABLE_PACKET_CHAIN */
1016
1017 /* Trim partial data in the packet. */
1018 packet_ptr -> nx_packet_length -= remaining_bytes;
1019 current_packet -> nx_packet_prepend_ptr += remaining_bytes;
1020
1021 /* Release the protection. */
1022 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
1023
1024 /* Regain exclusive access to IP instance. */
1025 tx_mutex_get(&(ip_ptr -> nx_ip_protection), TX_WAIT_FOREVER);
1026 }
1027 }
1028 else if ((wait_option) && (_tx_thread_current_ptr != &(ip_ptr -> nx_ip_thread)))
1029 {
1030
1031 /* Suspend the thread on this socket's transmit queue. */
1032
1033 /* Save the return packet pointer address as well. */
1034 _tx_thread_current_ptr -> tx_thread_additional_suspend_info = (VOID *)packet_ptr;
1035
1036 /* Add debug information. */
1037 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
1038
1039 /* Increment the suspended thread count. */
1040 socket_ptr -> nx_tcp_socket_transmit_suspended_count++;
1041
1042 if (socket_ptr -> nx_tcp_socket_zero_window_probe_has_data == NX_FALSE)
1043 {
1044
1045 /* Set data for zero window probe. */
1046 socket_ptr -> nx_tcp_socket_zero_window_probe_has_data = NX_TRUE;
1047 socket_ptr -> nx_tcp_socket_zero_window_probe_data = *(packet_ptr -> nx_packet_prepend_ptr);
1048 socket_ptr -> nx_tcp_socket_zero_window_probe_sequence = socket_ptr -> nx_tcp_socket_tx_sequence;
1049 socket_ptr -> nx_tcp_socket_zero_window_probe_failure = 0;
1050 }
1051
1052 /* Need preemption? */
1053 if (preempted == NX_FALSE)
1054 {
1055 UINT ip_thread_priority;
1056
1057 /* Yes. It will be able to send packet out immediately TCP window is non zero. */
1058 tx_thread_info_get(&ip_ptr -> nx_ip_thread, NX_NULL, NX_NULL, NX_NULL, &ip_thread_priority, NX_NULL,
1059 NX_NULL, NX_NULL, NX_NULL);
1060
1061 /*lint -e{644} suppress variable might not be initialized, since "ip_thread_priority" was initialized before TCP is enabled. */
1062 tx_thread_preemption_change(_tx_thread_current_ptr, ip_thread_priority, &old_threshold);
1063 preempted = NX_TRUE;
1064 }
1065
1066 /* Suspend the thread on the transmit suspension list. */
1067 _nx_tcp_socket_thread_suspend(&(socket_ptr -> nx_tcp_socket_transmit_suspension_list), _nx_tcp_transmit_cleanup, socket_ptr, &(ip_ptr -> nx_ip_protection), wait_option);
1068
1069 /* Determine if the send request was successful. */
1070 if (_tx_thread_current_ptr -> tx_thread_suspend_status)
1071 {
1072
1073 /* Restore preemption. */
1074 tx_thread_preemption_change(_tx_thread_current_ptr, old_threshold, &old_threshold);
1075
1076 /* Add debug information. */
1077 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
1078
1079 /* Just return the error code. */
1080 return(_tx_thread_current_ptr -> tx_thread_suspend_status);
1081 }
1082 }
1083 else
1084 {
1085
1086 /* Check advertised window. */
1087 if (socket_ptr -> nx_tcp_socket_zero_window_probe_has_data == NX_FALSE)
1088 {
1089
1090 /* Set data for zero window probe. */
1091 socket_ptr -> nx_tcp_socket_zero_window_probe_has_data = NX_TRUE;
1092 socket_ptr -> nx_tcp_socket_zero_window_probe_data = *(packet_ptr -> nx_packet_prepend_ptr);
1093 socket_ptr -> nx_tcp_socket_zero_window_probe_sequence = socket_ptr -> nx_tcp_socket_tx_sequence;
1094 socket_ptr -> nx_tcp_socket_zero_window_probe_failure = 0;
1095 }
1096
1097 /* Determine which transmit error is present. */
1098 if (socket_ptr -> nx_tcp_socket_transmit_sent_count < socket_ptr -> nx_tcp_socket_transmit_queue_maximum)
1099 {
1100
1101 /* Release protection. */
1102 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
1103
1104 /* Add debug information. */
1105 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
1106
1107 /* Not a queue depth problem, return a window overflow error. */
1108 return(NX_WINDOW_OVERFLOW);
1109 }
1110 else
1111 {
1112
1113 /* Release protection. */
1114 tx_mutex_put(&(ip_ptr -> nx_ip_protection));
1115
1116 /* Add debug information. */
1117 NX_PACKET_DEBUG(__FILE__, __LINE__, packet_ptr);
1118
1119 /* Return a transmit queue exceeded error. */
1120 return(NX_TX_QUEUE_DEPTH);
1121 }
1122 }
1123 }
1124 }