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 /** USBX Component                                                        */
15 /**                                                                       */
16 /**   Device RNDIS Class                                                  */
17 /**                                                                       */
18 /**************************************************************************/
19 /**************************************************************************/
20 
21 #define UX_SOURCE_CODE
22 
23 
24 /* Include necessary system files.  */
25 
26 #include "ux_api.h"
27 #include "ux_device_class_rndis.h"
28 #include "ux_device_stack.h"
29 
30 
31 #if !defined(UX_DEVICE_STANDALONE)
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_device_class_rndis_bulkout_thread               PORTABLE C      */
37 /*                                                           6.3.0        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function is the thread of the rndis bulk out endpoint. It      */
45 /*    is waiting for the host to send data on the bulk out endpoint to    */
46 /*    the device.                                                         */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    rndis_class                             Address of rndis class      */
51 /*                                                container               */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    None                                                                */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    _ux_device_stack_transfer_request     Request transfer              */
60 /*    _ux_network_driver_packet_received    Process received packet       */
61 /*    _ux_utility_long_get                  Get 32-bit value              */
62 /*    _ux_device_thread_suspend             Suspend thread                */
63 /*    nx_packet_allocate                    Allocate NetX packet          */
64 /*    nx_packet_release                     Release NetX packet           */
65 /*                                                                        */
66 /*  CALLED BY                                                             */
67 /*                                                                        */
68 /*    ThreadX                                                             */
69 /*                                                                        */
70 /*  RELEASE HISTORY                                                       */
71 /*                                                                        */
72 /*    DATE              NAME                      DESCRIPTION             */
73 /*                                                                        */
74 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
75 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
76 /*                                            prefixed UX to MS_TO_TICK,  */
77 /*                                            verified memset and memcpy  */
78 /*                                            cases,                      */
79 /*                                            resulting in version 6.1    */
80 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
81 /*                                            refined macros names,       */
82 /*                                            resulting in version 6.1.10 */
83 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
84 /*                                            fixed standalone compile,   */
85 /*                                            resulting in version 6.1.11 */
86 /*  10-31-2022     Chaoqiong Xiao           Modified comment(s),          */
87 /*                                            used NX API to copy data,   */
88 /*                                            used linked NX IP pool,     */
89 /*                                            resulting in version 6.2.0  */
90 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
91 /*                                            added zero copy support,    */
92 /*                                            added a new mode to manage  */
93 /*                                            endpoint buffer in classes, */
94 /*                                            resulting in version 6.3.0  */
95 /*                                                                        */
96 /**************************************************************************/
_ux_device_class_rndis_bulkout_thread(ULONG rndis_class)97 VOID  _ux_device_class_rndis_bulkout_thread(ULONG rndis_class)
98 {
99 
100 UX_SLAVE_CLASS                  *class_ptr;
101 UX_SLAVE_CLASS_RNDIS            *rndis;
102 UX_SLAVE_DEVICE                 *device;
103 UX_SLAVE_TRANSFER               *transfer_request;
104 UINT                            status;
105 NX_PACKET                       *packet;
106 ULONG                           packet_payload;
107 USB_NETWORK_DEVICE_TYPE         *ux_nx_device;
108 
109     /* Cast properly the rndis instance.  */
110     UX_THREAD_EXTENSION_PTR_GET(class_ptr, UX_SLAVE_CLASS, rndis_class)
111 
112     /* Get the rndis instance from this class container.  */
113     rndis =  (UX_SLAVE_CLASS_RNDIS *) class_ptr -> ux_slave_class_instance;
114 
115     /* Get the pointer to the device.  */
116     device =  &_ux_system_slave -> ux_system_slave_device;
117 
118     /* This thread runs forever but can be suspended or resumed.  */
119     while(1)
120     {
121 
122         /* Select the transfer request associated with BULK OUT endpoint.   */
123         transfer_request =  &rndis -> ux_slave_class_rndis_bulkout_endpoint -> ux_slave_endpoint_transfer_request;
124 
125         /* As long as the device is in the CONFIGURED state.  */
126         while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
127         {
128 
129             /* Check if packet pool is ready.  */
130             if (rndis -> ux_slave_class_rndis_packet_pool == UX_NULL)
131             {
132 
133                 /* Get the network device handle.  */
134                 ux_nx_device = (USB_NETWORK_DEVICE_TYPE *)(rndis -> ux_slave_class_rndis_network_handle);
135 
136                 /* Get packet pool from IP instance (if available).  */
137                 if (ux_nx_device -> ux_network_device_ip_instance != UX_NULL)
138                 {
139                     rndis -> ux_slave_class_rndis_packet_pool = ux_nx_device -> ux_network_device_ip_instance -> nx_ip_default_packet_pool;
140                 }
141                 else
142                 {
143 
144                     /* Error trap.  */
145                     _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_ETH_PACKET_POOL_ERROR);
146 
147                     _ux_utility_delay_ms(UX_DEVICE_CLASS_RNDIS_PACKET_POOL_INST_WAIT);
148                     continue;
149                 }
150             }
151 
152             /* We can accept new reception. Get a NX Packet.  */
153             status =  nx_packet_allocate(rndis -> ux_slave_class_rndis_packet_pool, &packet,
154                                          NX_RECEIVE_PACKET, UX_MS_TO_TICK(UX_DEVICE_CLASS_RNDIS_PACKET_POOL_WAIT));
155 
156             if (status == NX_SUCCESS)
157             {
158 
159                 /* And length.  */
160                 transfer_request -> ux_slave_transfer_request_requested_length =  UX_DEVICE_CLASS_RNDIS_BULKOUT_BUFFER_SIZE;
161                 transfer_request -> ux_slave_transfer_request_actual_length =     0;
162 
163                 /* Memorize this packet at the beginning of the queue.  */
164                 rndis -> ux_slave_class_rndis_receive_queue = packet;
165 
166                 /* Reset the queue pointer of this packet.  */
167                 packet -> nx_packet_queue_next = UX_NULL;
168 
169 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_RNDIS_ZERO_COPY)
170 
171                 /* Adjust the prepend pointer to take into account the RNDIS header and
172                    non 3 bit alignment of the ethernet header.  */
173                 packet -> nx_packet_append_ptr += UX_DEVICE_CLASS_RNDIS_PACKET_BUFFER;
174                 switch(((ALIGN_TYPE)packet -> nx_packet_append_ptr) & 0x3)
175                 {
176                 case 0:
177                     packet -> nx_packet_append_ptr += 2;
178                     break;
179                 case 1:
180                     packet -> nx_packet_append_ptr += 1;
181                     break;
182                 case 3:
183                     packet -> nx_packet_append_ptr += 3;
184                     break;
185                 default:
186                     break;
187                 }
188                 packet -> nx_packet_prepend_ptr = packet -> nx_packet_append_ptr - UX_DEVICE_CLASS_RNDIS_PACKET_BUFFER;
189 
190                 /* Get actual size of transfer buffer.  */
191                 packet_payload = (ULONG)(packet -> nx_packet_data_end - packet -> nx_packet_prepend_ptr);
192 
193                 /* Send the request to the device controller.  */
194                 transfer_request -> ux_slave_transfer_request_data_pointer = packet -> nx_packet_prepend_ptr;
195                 status =  _ux_device_stack_transfer_request(transfer_request,
196                                     packet_payload, packet_payload);
197 #else
198 
199                 /* Send the request to the device controller.  */
200                 status =  _ux_device_stack_transfer_request(transfer_request, UX_DEVICE_CLASS_RNDIS_BULKOUT_BUFFER_SIZE,
201                                                                 UX_DEVICE_CLASS_RNDIS_BULKOUT_BUFFER_SIZE);
202 #endif
203 
204                 /* Check the completion code. */
205                 if (status == UX_SUCCESS)
206                 {
207 
208                     /* We only proceed with packets that are received OK, if error, ignore the packet. */
209                     /* If trace is enabled, insert this event into the trace buffer.  */
210                     UX_TRACE_IN_LINE_INSERT(UX_TRACE_DEVICE_CLASS_RNDIS_PACKET_RECEIVE, rndis, 0, 0, 0, UX_TRACE_DEVICE_CLASS_EVENTS, 0, 0)
211 
212                     /* Check the state of the transfer.  If there is an error, we do not proceed with this report.
213                        Ensure this packet is at least larger than the header.
214                        Also ensure the header has a valid ID of 1.  */
215                     if (transfer_request -> ux_slave_transfer_request_actual_length > UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH &&
216                         _ux_utility_long_get(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_MESSAGE_TYPE) == UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_MSG)
217                     {
218 
219                         /* Get the size of the payload.  */
220                         packet_payload =  _ux_utility_long_get(transfer_request -> ux_slave_transfer_request_data_pointer + UX_DEVICE_CLASS_RNDIS_PACKET_DATA_LENGTH);
221 
222                         /* Ensure the length reported in the RNDIS header is not larger than it actually is.
223                             The reason we can't check to see if the length reported in the header and the
224                             actual length are exactly equal is because there might other data after the payload
225                             (padding, or even a message). */
226                         if (packet_payload <= transfer_request -> ux_slave_transfer_request_actual_length - UX_DEVICE_CLASS_RNDIS_PACKET_HEADER_LENGTH)
227                         {
228 
229 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_RNDIS_ZERO_COPY)
230 
231                             /* Data already in buffer, adjust packet start and save length.  */
232                             packet -> nx_packet_prepend_ptr += UX_DEVICE_CLASS_RNDIS_PACKET_BUFFER;
233                             packet -> nx_packet_length = packet_payload;
234                             packet -> nx_packet_append_ptr += packet -> nx_packet_length;
235 
236                             /* Send that packet to the NetX USB broker.  */
237                             _ux_network_driver_packet_received(rndis -> ux_slave_class_rndis_network_handle, packet);
238 #else
239 
240                             /* Adjust the prepend pointer to take into account the non 3 bit alignment of the ethernet header.  */
241                             packet -> nx_packet_prepend_ptr += sizeof(USHORT);
242                             packet -> nx_packet_append_ptr += sizeof(USHORT);
243 
244                             /* Copy the received packet in the IP packet data area.  */
245                             status = nx_packet_data_append(packet,
246                                     transfer_request -> ux_slave_transfer_request_data_pointer +
247                                                             UX_DEVICE_CLASS_RNDIS_PACKET_BUFFER,
248                                     packet_payload,
249                                     rndis -> ux_slave_class_rndis_packet_pool,
250                                     UX_MS_TO_TICK(UX_DEVICE_CLASS_RNDIS_PACKET_POOL_WAIT));
251                             if (status == UX_SUCCESS)
252                             {
253 
254                                 /* Send that packet to the NetX USB broker.  */
255                                 _ux_network_driver_packet_received(rndis -> ux_slave_class_rndis_network_handle, packet);
256                             }
257                             else
258                             {
259 
260                                 /* Error.  */
261                                 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_ETH_PACKET_ERROR);
262                                 nx_packet_release(packet);
263                             }
264 #endif
265                         }
266                         else
267                         {
268 
269                             /* We received a malformed packet. Report to application.  */
270                             _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR);
271                             nx_packet_release(packet);
272                         }
273                     }
274                     else
275                     {
276 
277                         /* We received a malformed packet. Report to application.  */
278                         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_CLASS_MALFORMED_PACKET_RECEIVED_ERROR);
279                         nx_packet_release(packet);
280                     }
281                 }
282                 else
283                 {
284 
285                     /* Free the packet that was not successfully received.  */
286                     nx_packet_release(packet);
287                 }
288 
289             }
290             else
291             {
292 
293                 /* Packet allocation timed out. Note that the timeout value is
294                    configurable.  */
295 
296                 /* Error trap. No need for trace, since NetX does it.  */
297                 _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, UX_MEMORY_INSUFFICIENT);
298             }
299         }
300 
301         /* We need to suspend ourselves. We will be resumed by the device enumeration module.  */
302         _ux_device_thread_suspend(&rndis -> ux_slave_class_rndis_bulkout_thread);
303     }
304 }
305 #endif
306