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_thread PORTABLE C */
38 /* 6.2.0 */
39 /* AUTHOR */
40 /* */
41 /* Chaoqiong Xiao, Microsoft Corporation */
42 /* */
43 /* DESCRIPTION */
44 /* */
45 /* This is the CDC ECM thread that monitors the link change flag, */
46 /* receives data from the device, and passes the data to the NetX-USB */
47 /* broker. */
48 /* */
49 /* INPUT */
50 /* */
51 /* cdc_ecm CDC ECM instance */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* Completion Status */
56 /* */
57 /* CALLS */
58 /* */
59 /* _ux_host_class_cdc_ecm_transmit_queue_clean */
60 /* Clean transmit queue */
61 /* _ux_host_stack_transfer_request Transfer request */
62 /* _ux_host_semaphore_get Get semaphore */
63 /* _ux_host_semaphore_put Put semaphore */
64 /* _ux_utility_short_get_big_endian Get 16-bit big endian */
65 /* _ux_network_driver_link_up Set state link up */
66 /* _ux_network_driver_link_down Set state link down */
67 /* _ux_network_driver_packet_received Process received packet */
68 /* nx_packet_allocate Allocate NetX packet */
69 /* nx_packet_release Free NetX packet */
70 /* */
71 /* CALLED BY */
72 /* */
73 /* CDC ECM class initialization */
74 /* */
75 /* RELEASE HISTORY */
76 /* */
77 /* DATE NAME DESCRIPTION */
78 /* */
79 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
80 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
81 /* prefixed UX to MS_TO_TICK, */
82 /* used UX prefix to refer to */
83 /* TX symbols instead of using */
84 /* them directly, */
85 /* resulting in version 6.1 */
86 /* 02-02-2021 Xiuwen Cai Modified comment(s), added */
87 /* compile option for using */
88 /* packet pool from NetX, */
89 /* resulting in version 6.1.4 */
90 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
91 /* refined macros names, */
92 /* resulting in version 6.1.10 */
93 /* 04-25-2022 Chaoqiong Xiao Modified comment(s), */
94 /* fixed standalone compile, */
95 /* resulting in version 6.1.11 */
96 /* 10-31-2022 Chaoqiong Xiao Modified comment(s), */
97 /* no length from IP header, */
98 /* deprecated ECM pool option, */
99 /* supported NX packet chain, */
100 /* resulting in version 6.2.0 */
101 /* */
102 /**************************************************************************/
_ux_host_class_cdc_ecm_thread(ULONG parameter)103 VOID _ux_host_class_cdc_ecm_thread(ULONG parameter)
104 {
105
106 UX_HOST_CLASS_CDC_ECM *cdc_ecm;
107 UX_TRANSFER *transfer_request;
108 NX_PACKET *packet;
109 UINT status;
110 USB_NETWORK_DEVICE_TYPE *usb_network_device_ptr;
111 ULONG packet_buffer_size;
112
113
114 /* Cast the parameter passed in the thread into the cdc_ecm pointer. */
115 UX_THREAD_EXTENSION_PTR_GET(cdc_ecm, UX_HOST_CLASS_CDC_ECM, parameter)
116
117 /* Loop forever waiting for changes signaled through the semaphore. */
118 while (1)
119 {
120
121 /* Wait for the semaphore to be put by the cdc_ecm interrupt event. */
122 _ux_host_semaphore_get_norc(&cdc_ecm -> ux_host_class_cdc_ecm_interrupt_notification_semaphore, UX_WAIT_FOREVER);
123
124 /* Check the link state. It is either pending up or down. */
125 if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_PENDING_UP)
126 {
127
128 /* Now the link is up. */
129 cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP;
130
131 /* Communicate the state with the network driver. */
132 _ux_network_driver_link_up(cdc_ecm -> ux_host_class_cdc_ecm_network_handle);
133
134 /* As long as we are connected, configured and link up ... do some work.... */
135 while ((cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP) &&
136 (cdc_ecm -> ux_host_class_cdc_ecm_device -> ux_device_state == UX_DEVICE_CONFIGURED))
137 {
138
139 /* Check if we have packet pool available. */
140 if (cdc_ecm -> ux_host_class_cdc_ecm_packet_pool == UX_NULL)
141 {
142
143 /* Get the network device handle. */
144 usb_network_device_ptr = (USB_NETWORK_DEVICE_TYPE *)(cdc_ecm -> ux_host_class_cdc_ecm_network_handle);
145
146 /* Check if IP instance is available. */
147 if (usb_network_device_ptr -> ux_network_device_ip_instance != UX_NULL)
148 {
149
150 /* Get the packet pool from IP instance. */
151 cdc_ecm -> ux_host_class_cdc_ecm_packet_pool = usb_network_device_ptr -> ux_network_device_ip_instance -> nx_ip_default_packet_pool;
152 }
153 else
154 {
155
156 /* IP instance is not available, wait for application to attach the interface. */
157 _ux_utility_delay_ms(UX_MS_TO_TICK(UX_HOST_CLASS_CDC_ECM_PACKET_POOL_INSTANCE_WAIT));
158 }
159 continue;
160 }
161
162 /* We can accept reception. Get a NX Packet. */
163 status = nx_packet_allocate(cdc_ecm -> ux_host_class_cdc_ecm_packet_pool, &packet,
164 NX_RECEIVE_PACKET, UX_MS_TO_TICK(UX_HOST_CLASS_CDC_ECM_PACKET_POOL_WAIT));
165
166 if (status == NX_SUCCESS)
167 {
168
169 /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header. */
170 packet -> nx_packet_prepend_ptr += sizeof(USHORT);
171
172 /* We have a packet. Link this packet to the reception transfer request on the bulk in endpoint. */
173 transfer_request = &cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_endpoint -> ux_endpoint_transfer_request;
174
175 #ifdef UX_HOST_CLASS_CDC_ECM_PACKET_CHAIN_SUPPORT
176
177 /* Check packet buffer size, if too small chain is used. */
178 packet_buffer_size = (ULONG)(packet -> nx_packet_data_end - packet -> nx_packet_prepend_ptr);
179 if (packet_buffer_size < UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE)
180 {
181 if (cdc_ecm -> ux_host_class_cdc_ecm_receive_buffer == UX_NULL)
182 {
183 cdc_ecm -> ux_host_class_cdc_ecm_receive_buffer =
184 _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY,
185 UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE);
186 if (cdc_ecm -> ux_host_class_cdc_ecm_receive_buffer == UX_NULL)
187 {
188
189 /* Memory allocation fail. */
190 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT);
191
192 /* Release packet. */
193 nx_packet_release(packet);
194
195 /* Delay to let other threads to run. */
196 _ux_utility_delay_ms(1);
197 continue;
198 }
199
200 }
201
202 /* Set the data pointer. */
203 transfer_request -> ux_transfer_request_data_pointer = cdc_ecm -> ux_host_class_cdc_ecm_receive_buffer;
204 }
205 else
206 #endif
207 {
208
209 /* Set the data pointer. */
210 transfer_request -> ux_transfer_request_data_pointer = packet -> nx_packet_prepend_ptr;
211
212 }
213
214 /* And length. */
215 transfer_request -> ux_transfer_request_requested_length = UX_HOST_CLASS_CDC_ECM_NX_PAYLOAD_SIZE;
216 transfer_request -> ux_transfer_request_actual_length = 0;
217
218 /* Store the packet that owns this transaction. */
219 transfer_request -> ux_transfer_request_user_specific = packet;
220
221 /* Reset the queue pointer of this packet. */
222 packet -> nx_packet_queue_next = UX_NULL;
223
224 /* We're arming the transfer now. */
225 cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process = UX_TRUE;
226
227 /* Is the link up? */
228 if (cdc_ecm -> ux_host_class_cdc_ecm_link_state == UX_HOST_CLASS_CDC_ECM_LINK_STATE_UP)
229 {
230
231 /* Ask USB to schedule a reception. */
232 status = _ux_host_stack_transfer_request(transfer_request);
233
234 /* Signal that we are done arming and resume waiting thread if necessary. */
235 cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process = UX_FALSE;
236 if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish == UX_TRUE)
237 _ux_host_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore);
238
239 /* Check if the transaction was armed successfully. */
240 if (status == UX_SUCCESS)
241 {
242
243 /* Wait for the completion of the transfer request. */
244 _ux_host_semaphore_get_norc(&transfer_request -> ux_transfer_request_semaphore, UX_WAIT_FOREVER);
245
246 /* Check the transfer status. If there is a transport error, we ignore the packet
247 and restart it. */
248 if (transfer_request -> ux_transfer_request_completion_code == UX_SUCCESS)
249 {
250
251 #ifdef UX_HOST_CLASS_CDC_ECM_PACKET_CHAIN_SUPPORT
252
253 /* Check if transfer buffer is used. */
254 if (packet -> nx_packet_prepend_ptr !=
255 transfer_request -> ux_transfer_request_data_pointer)
256 {
257
258 /* Adjust append_ptr for copy. */
259 packet -> nx_packet_append_ptr = packet -> nx_packet_prepend_ptr;
260
261 /* Append data to packet. */
262 status = nx_packet_data_append(packet,
263 transfer_request -> ux_transfer_request_data_pointer,
264 transfer_request -> ux_transfer_request_actual_length,
265 cdc_ecm -> ux_host_class_cdc_ecm_packet_pool,
266 UX_MS_TO_TICK(UX_HOST_CLASS_CDC_ECM_PACKET_POOL_WAIT));
267 if (status != NX_SUCCESS)
268 {
269
270 /* Release packet. */
271 nx_packet_release(packet);
272
273 /* Error trap. */
274 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_ETH_PACKET_ERROR);
275 continue;
276 }
277 }
278 else
279 #endif
280 {
281
282 /* Get the packet length. */
283 packet -> nx_packet_length = transfer_request -> ux_transfer_request_actual_length;
284
285 /* Adjust the prepend, length, and append fields. */
286 packet -> nx_packet_append_ptr =
287 packet->nx_packet_prepend_ptr + transfer_request -> ux_transfer_request_actual_length;
288 }
289
290 /* Send that packet to the NetX USB broker. */
291 _ux_network_driver_packet_received(cdc_ecm -> ux_host_class_cdc_ecm_network_handle, packet);
292 }
293 else
294 {
295
296 /* Free the packet that was not successfully received. */
297 nx_packet_release(packet);
298 }
299 }
300 else
301 {
302
303 /* Error arming transfer. */
304
305 /* Release packet. */
306 nx_packet_release(packet);
307 }
308 }
309 else
310 {
311
312 /* Link is down. */
313
314 /* Signal that we are done arming and resume waiting thread if necessary. */
315 cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_check_and_arm_in_process = UX_FALSE;
316 if (cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish == UX_TRUE)
317 _ux_host_semaphore_put(&cdc_ecm -> ux_host_class_cdc_ecm_bulk_in_transfer_waiting_for_check_and_arm_to_finish_semaphore);
318
319 /* Release packet. */
320 nx_packet_release(packet);
321 }
322 }
323 else
324 {
325
326 /* Packet allocation timed out. Note that the timeout value is
327 configurable. */
328
329 /* Error trap. No need for trace, since NetX does it. */
330 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT);
331 }
332 }
333 }
334 else
335 {
336
337 /* The link state is pending down. We need to free the xmit queue. */
338 _ux_host_class_cdc_ecm_transmit_queue_clean(cdc_ecm);
339
340 /* Link state can now be set to down. */
341
342 /* Notify the network driver. */
343 _ux_network_driver_link_down(cdc_ecm -> ux_host_class_cdc_ecm_network_handle);
344
345 /* Set the link state. */
346 cdc_ecm -> ux_host_class_cdc_ecm_link_state = UX_HOST_CLASS_CDC_ECM_LINK_STATE_DOWN;
347 }
348 }
349 }
350 #endif
351