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 /** USBX Component */
16 /** */
17 /** CDC ECM Class */
18 /** */
19 /**************************************************************************/
20 /**************************************************************************/
21
22
23 /* Include necessary system files. */
24
25 #define UX_SOURCE_CODE
26
27 #include "ux_api.h"
28 #include "ux_host_class_cdc_ecm.h"
29 #include "ux_host_stack.h"
30
31
32 #if !defined(UX_HOST_STANDALONE)
33 /**************************************************************************/
34 /* */
35 /* FUNCTION RELEASE */
36 /* */
37 /* _ux_host_class_cdc_ecm_write PORTABLE C */
38 /* 6.2.0 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This function writes to the cdc_ecm interface. The call is */
46 /* non-blocking and queues the packet if there is an on-going write. */
47 /* */
48 /* INPUT */
49 /* */
50 /* cdc_ecm Pointer to cdc_ecm class */
51 /* packet Packet to write or queue */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* Completion Status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _ux_host_stack_transfer_request Process transfer request */
60 /* _ux_host_semaphore_put Release protection semaphore */
61 /* nx_packet_transmit_release Release NetX packet */
62 /* */
63 /* CALLED BY */
64 /* */
65 /* Application */
66 /* */
67 /* RELEASE HISTORY */
68 /* */
69 /* DATE NAME DESCRIPTION */
70 /* */
71 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
72 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
73 /* used UX prefix to refer to */
74 /* TX symbols instead of using */
75 /* them directly, */
76 /* resulting in version 6.1 */
77 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
78 /* refined macros names, */
79 /* resulting in version 6.1.10 */
80 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
81 /* fixed standalone compile, */
82 /* resulting in version 6.1.11 */
83 /* 10-31-2022 Chaoqiong Xiao Modified comment(s), */
84 /* supported NX packet chain, */
85 /* resulting in version 6.2.0 */
86 /* */
87 /**************************************************************************/
_ux_host_class_cdc_ecm_write(VOID * cdc_ecm_class,NX_PACKET * packet)88 UINT _ux_host_class_cdc_ecm_write(VOID *cdc_ecm_class, NX_PACKET *packet)
89 {
90
91 UX_INTERRUPT_SAVE_AREA
92
93 UX_TRANSFER *transfer_request;
94 UINT status;
95 UCHAR *packet_header;
96 UX_HOST_CLASS_CDC_ECM *cdc_ecm;
97 #ifdef UX_HOST_CLASS_CDC_ECM_PACKET_CHAIN_SUPPORT
98 ULONG copied;
99 #endif
100
101 /* Get the instance. */
102 cdc_ecm = (UX_HOST_CLASS_CDC_ECM *) cdc_ecm_class;
103
104 /* If trace is enabled, insert this event into the trace buffer. */
105 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ECM_WRITE, cdc_ecm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
106
107 /* We're arming transfer now. */
108 cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_check_and_arm_in_process = UX_TRUE;
109
110 /* We need to disable interrupts here because we need to make sure that if the xmit
111 queue is non-null, it remains non-null until we have queued the packet.
112 Note that we do not have to worry about the case where the queue is null,
113 because we are the only ones that can change it from null to non-null. */
114 UX_DISABLE
115
116 /* Ensure the instance is valid. */
117 if (cdc_ecm -> ux_host_class_cdc_ecm_state != UX_HOST_CLASS_INSTANCE_LIVE)
118 {
119
120 /* Restore interrupts. */
121 UX_RESTORE
122
123 /* Error trap. */
124 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_HOST_CLASS_INSTANCE_UNKNOWN);
125
126 /* If trace is enabled, insert this event into the trace buffer. */
127 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_HOST_CLASS_INSTANCE_UNKNOWN, cdc_ecm, 0, 0, UX_TRACE_ERRORS, 0, 0)
128
129 return(UX_HOST_CLASS_INSTANCE_UNKNOWN);
130 }
131
132 /* Validate packet length. */
133 if (packet -> nx_packet_length > UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE)
134 {
135
136 /* Restore interrupts. */
137 UX_RESTORE
138
139 /* Error trap. */
140 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_ETH_SIZE_ERROR);
141
142 /* If trace is enabled, insert this event into the trace buffer. */
143 UX_TRACE_IN_LINE_INSERT(UX_TRACE_ERROR, UX_CLASS_ETH_SIZE_ERROR, cdc_ecm, packet -> nx_packet_length, 0, UX_TRACE_ERRORS, 0, 0)
144
145 return(UX_CLASS_ETH_SIZE_ERROR);
146 }
147
148 /* Are we in a valid state? */
149 if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP)
150 {
151
152 /* Check the queue. See if there is something that is being sent. */
153 if (cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head == UX_NULL)
154 {
155
156 /* Reset the queue pointer of this packet. */
157 packet -> nx_packet_queue_next = UX_NULL;
158
159 /* Memorize this packet at the beginning of the queue. */
160 cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head = packet;
161 cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_tail = packet;
162
163 /* Restore interrupts. */
164 UX_RESTORE
165
166 /* Now we need to arm the transfer. */
167
168 /* Get the pointer to the bulk out endpoint transfer request. */
169 transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_endpoint -> ux_endpoint_transfer_request;
170
171 #ifdef UX_HOST_CLASS_CDC_ECM_PACKET_CHAIN_SUPPORT
172
173 if (packet -> nx_packet_next != UX_NULL)
174 {
175
176 /* Create buffer. */
177 if (cdc_ecm -> ux_host_class_cdc_ecm_xmit_buffer == UX_NULL)
178 {
179 cdc_ecm -> ux_host_class_cdc_ecm_xmit_buffer = _ux_utility_memory_allocate(UX_NO_ALIGN,
180 UX_CACHE_SAFE_MEMORY, UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE);
181 if (cdc_ecm -> ux_host_class_cdc_ecm_xmit_buffer == UX_NULL)
182 {
183 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT);
184 return(UX_MEMORY_INSUFFICIENT);
185 }
186 }
187
188 /* Put packet to continuous buffer to transfer. */
189 packet_header = cdc_ecm -> ux_host_class_cdc_ecm_xmit_buffer;
190 nx_packet_data_extract_offset(packet, 0, packet_header, packet -> nx_packet_length, &copied);
191 }
192 else
193 #endif
194 {
195
196 /* Load the address of the current packet header at the physical header. */
197 packet_header = packet -> nx_packet_prepend_ptr;
198
199 }
200
201 /* Setup the transaction parameters. */
202 transfer_request -> ux_transfer_request_data_pointer = packet_header;
203 transfer_request -> ux_transfer_request_requested_length = packet -> nx_packet_length;
204
205 /* Store the packet that owns this transaction. */
206 transfer_request -> ux_transfer_request_user_specific = packet;
207
208 /* Arm the transfer request. */
209 status = _ux_host_stack_transfer_request(transfer_request);
210
211 /* Did we successfully arm the transfer? */
212 if (status != UX_SUCCESS)
213 {
214
215 /* Clear the queue. No need to clear the tail. */
216 cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_head = UX_NULL;
217
218 /* We cleared the queue, so we must free the packet. First
219 we need to clean it before passing it to NetX. */
220 packet -> nx_packet_prepend_ptr = packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE;
221 packet -> nx_packet_length = packet -> nx_packet_length - UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE;
222
223 /* And ask Netx to release it. */
224 nx_packet_transmit_release(packet);
225
226 /* Could not arm this transfer. */
227 status = UX_ERROR;
228 }
229 }
230 else
231 {
232
233 /* The packet to be sent is the last in the chain. */
234 packet -> nx_packet_queue_next = NX_NULL;
235
236 /* Memorize the packet to be sent. */
237 cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_tail -> nx_packet_queue_next = packet;
238
239 /* Set the tail. */
240 cdc_ecm -> ux_host_class_cdc_ecm_xmit_queue_tail = packet;
241
242 /* Restore interrupts. */
243 UX_RESTORE
244
245 /* Successfully added to queue. */
246 status = UX_SUCCESS;
247 }
248 }
249 else
250 {
251
252 /* Link was down. */
253
254 /* Restore interrupts. */
255 UX_RESTORE
256
257 /* Release the packet. */
258 packet -> nx_packet_prepend_ptr = packet -> nx_packet_prepend_ptr + UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE;
259 packet -> nx_packet_length = packet -> nx_packet_length - UX_HOST_CLASS_CDC_ECM_ETHERNET_SIZE;
260 nx_packet_transmit_release(packet);
261
262 /* Report error to application. */
263 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_CDC_ECM_LINK_STATE_DOWN_ERROR);
264
265 /* Return error. */
266 status = UX_ERROR;
267 }
268
269 /* Signal that we are done arming and resume waiting thread if necessary. */
270 cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_check_and_arm_in_process = UX_FALSE;
271 if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish == UX_TRUE)
272 _ux_host_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_out_transfer_waiting_for_check_and_arm_to_finish_semaphore);
273
274 /* We are done here. */
275 return(status);
276 }
277 #endif
278