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