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