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