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 /** USBX Component                                                        */
14 /**                                                                       */
15 /**   Device RNDIS Class                                                  */
16 /**                                                                       */
17 /**************************************************************************/
18 /**************************************************************************/
19 
20 #define UX_SOURCE_CODE
21 
22 
23 /* Include necessary system files.  */
24 
25 #include "ux_api.h"
26 #include "ux_device_class_rndis.h"
27 #include "ux_device_stack.h"
28 
29 UX_DEVICE_CLASS_RNDIS_NX_ETHERNET_POOL_ALLOCSIZE_ASSERT
30 
31 /* Define list of supported OIDs (ends with zero-terminator) */
32 ULONG ux_device_class_rndis_oid_supported_list[UX_DEVICE_CLASS_RNDIS_OID_SUPPORTED_LIST_LENGTH + 1] =
33 {
34     /* Mandatory general OIDs. */
35     UX_DEVICE_CLASS_RNDIS_OID_GEN_SUPPORTED_LIST,
36     UX_DEVICE_CLASS_RNDIS_OID_GEN_HARDWARE_STATUS,
37     UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_SUPPORTED,
38     UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_IN_USE,
39     UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
40     UX_DEVICE_CLASS_RNDIS_OID_GEN_LINK_SPEED,
41     UX_DEVICE_CLASS_RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
42     UX_DEVICE_CLASS_RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
43     UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_ID,
44     UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DESCRIPTION,
45     UX_DEVICE_CLASS_RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
46     UX_DEVICE_CLASS_RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
47     UX_DEVICE_CLASS_RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
48     UX_DEVICE_CLASS_RNDIS_OID_GEN_MAC_OPTIONS,
49     UX_DEVICE_CLASS_RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
50     UX_DEVICE_CLASS_RNDIS_OID_GEN_PHYSICAL_MEDIUM,
51     UX_DEVICE_CLASS_RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER,
52 
53     /* Mandatory statistical OIDs. */
54     UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_OK,
55     UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_OK,
56     UX_DEVICE_CLASS_RNDIS_OID_GEN_XMIT_ERROR,
57     UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_ERROR,
58     UX_DEVICE_CLASS_RNDIS_OID_GEN_RCV_NO_BUFFER,
59 
60     /* Mandatory 802.3 OIDs.  */
61     UX_DEVICE_CLASS_RNDIS_OID_802_3_PERMANENT_ADDRESS,
62     UX_DEVICE_CLASS_RNDIS_OID_802_3_CURRENT_ADDRESS,
63     UX_DEVICE_CLASS_RNDIS_OID_802_3_MULTICAST_LIST,
64     UX_DEVICE_CLASS_RNDIS_OID_802_3_MAC_OPTIONS,
65     UX_DEVICE_CLASS_RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
66 
67     /* Mandatory 802.3 statistical OIDs. */
68     UX_DEVICE_CLASS_RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
69     UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_ONE_COLLISION,
70     UX_DEVICE_CLASS_RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
71 
72     0,
73 };
74 
75 /**************************************************************************/
76 /*                                                                        */
77 /*  FUNCTION                                               RELEASE        */
78 /*                                                                        */
79 /*    _ux_device_class_rndis_initialize                   PORTABLE C      */
80 /*                                                           6.3.0        */
81 /*  AUTHOR                                                                */
82 /*                                                                        */
83 /*    Chaoqiong Xiao, Microsoft Corporation                               */
84 /*                                                                        */
85 /*  DESCRIPTION                                                           */
86 /*                                                                        */
87 /*    This function initializes the USB RNDIS device.                     */
88 /*                                                                        */
89 /*  INPUT                                                                 */
90 /*                                                                        */
91 /*    command                               Pointer to rndis command      */
92 /*                                                                        */
93 /*  OUTPUT                                                                */
94 /*                                                                        */
95 /*    Completion Status                                                   */
96 /*                                                                        */
97 /*  CALLS                                                                 */
98 /*                                                                        */
99 /*    _ux_utility_memory_allocate           Allocate memory               */
100 /*    _ux_utility_memory_copy               Copy memory                   */
101 /*    _ux_utility_memory_free               Free memory                   */
102 /*    _ux_utility_event_flags_create        Create Flag group             */
103 /*    _ux_utility_event_flags_delete        Delete Flag group             */
104 /*    _ux_utility_mutex_create              Create mutex                  */
105 /*    _ux_device_mutex_delete               Delete mutex                  */
106 /*    _ux_device_semaphore_create           Create semaphore              */
107 /*    _ux_device_semaphore_delete           Delete semaphore              */
108 /*    _ux_device_thread_create              Create thread                 */
109 /*    _ux_device_thread_delete              Delete thread                 */
110 /*    nx_packet_pool_create                 Create NetX packet pool       */
111 /*    nx_packet_pool_delete                 Delete NetX packet pool       */
112 /*                                                                        */
113 /*  CALLED BY                                                             */
114 /*                                                                        */
115 /*    USBX Source Code                                                    */
116 /*                                                                        */
117 /*  RELEASE HISTORY                                                       */
118 /*                                                                        */
119 /*    DATE              NAME                      DESCRIPTION             */
120 /*                                                                        */
121 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
122 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
123 /*                                            verified memset and memcpy  */
124 /*                                            cases, used UX prefix to    */
125 /*                                            refer to TX symbols instead */
126 /*                                            of using them directly,     */
127 /*                                            resulting in version 6.1    */
128 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
129 /*                                            refined macros names,       */
130 /*                                            resulting in version 6.1.10 */
131 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
132 /*                                            fixed standalone compile,   */
133 /*                                            resulting in version 6.1.11 */
134 /*  07-29-2022     Chaoqiong Xiao           Modified comment(s),          */
135 /*                                            fixed parameter/variable    */
136 /*                                            names conflict C++ keyword, */
137 /*                                            resulting in version 6.1.12 */
138 /*  10-31-2022     Chaoqiong Xiao           Modified comment(s),          */
139 /*                                            removed internal NX pool,   */
140 /*                                            resulting in version 6.2.0  */
141 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
142 /*                                            added a new mode to manage  */
143 /*                                            endpoint buffer in classes, */
144 /*                                            checked compile options,    */
145 /*                                            resulting in version 6.3.0  */
146 /*                                                                        */
147 /**************************************************************************/
_ux_device_class_rndis_initialize(UX_SLAVE_CLASS_COMMAND * command)148 UINT  _ux_device_class_rndis_initialize(UX_SLAVE_CLASS_COMMAND *command)
149 {
150 #if defined(UX_DEVICE_STANDALONE)
151     UX_PARAMETER_NOT_USED(command);
152     return(UX_FUNCTION_NOT_SUPPORTED);
153 #else
154 
155 UX_SLAVE_CLASS_RNDIS                        *rndis;
156 UX_SLAVE_CLASS_RNDIS_PARAMETER              *rndis_parameter;
157 UX_SLAVE_CLASS                              *class_ptr;
158 UINT                                        status = UX_SUCCESS;
159 
160 
161     /* Compile option checks.  */
162     UX_ASSERT(UX_DEVICE_CLASS_RNDIS_MAX_CONTROL_RESPONSE_LENGTH <= UX_SLAVE_REQUEST_CONTROL_MAX_LENGTH);
163 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 0
164     UX_ASSERT(UX_DEVICE_CLASS_RNDIS_BULKOUT_BUFFER_SIZE <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
165     UX_ASSERT(UX_DEVICE_CLASS_RNDIS_BULKIN_BUFFER_SIZE <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
166     UX_ASSERT(UX_DEVICE_CLASS_RNDIS_INTERRUPTIN_BUFFER_SIZE <= UX_SLAVE_REQUEST_DATA_MAX_LENGTH);
167 #endif
168 
169     /* Get the class container.  */
170     class_ptr =  command -> ux_slave_class_command_class_ptr;
171 
172     /* Create an instance of the device rndis class.  */
173     rndis =  _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, sizeof(UX_SLAVE_CLASS_RNDIS));
174 
175     /* Check for successful allocation.  */
176     if (rndis == UX_NULL)
177         return(UX_MEMORY_INSUFFICIENT);
178 
179     /* Save the address of the RNDIS instance inside the RNDIS container.  */
180     class_ptr -> ux_slave_class_instance = (VOID *) rndis;
181 
182     /* Get the pointer to the application parameters for the rndis class.  */
183     rndis_parameter =  command -> ux_slave_class_command_parameter;
184 
185     /* Store the start and stop signals if needed by the application.  */
186     rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_activate = rndis_parameter -> ux_slave_class_rndis_instance_activate;
187     rndis -> ux_slave_class_rndis_parameter.ux_slave_class_rndis_instance_deactivate = rndis_parameter -> ux_slave_class_rndis_instance_deactivate;
188 
189     /* Save the Netx IP passed by the application.  */
190     rndis -> ux_slave_class_rndis_nx_ip = rndis_parameter -> ux_slave_class_rndis_parameter_nx_ip;
191 
192     /* Save the Netx IP address passed by the application.  */
193     rndis -> ux_slave_class_rndis_nx_ip_address = rndis_parameter -> ux_slave_class_rndis_parameter_nx_ip_address;
194 
195     /* Save the Netx IP address network mask passed by the application.  */
196     rndis -> ux_slave_class_rndis_nx_ip_network_mask = rndis_parameter -> ux_slave_class_rndis_parameter_nx_ip_network_mask;
197 
198     /* Copy the local node ID.  */
199     _ux_utility_memory_copy(rndis -> ux_slave_class_rndis_local_node_id, rndis_parameter -> ux_slave_class_rndis_parameter_local_node_id,
200                             UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH); /* Use case of memcpy is verified. */
201 
202     /* Copy the remote node ID.  */
203     _ux_utility_memory_copy(rndis -> ux_slave_class_rndis_remote_node_id, rndis_parameter -> ux_slave_class_rndis_parameter_remote_node_id,
204                             UX_DEVICE_CLASS_RNDIS_NODE_ID_LENGTH); /* Use case of memcpy is verified. */
205 
206     /* Store the rest of the parameters as they are in the local instance.  */
207     _ux_utility_memory_copy(&rndis -> ux_slave_class_rndis_parameter, rndis_parameter, sizeof (UX_SLAVE_CLASS_RNDIS_PARAMETER)); /* Use case of memcpy is verified. */
208 
209 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
210 
211     /* Allocate memory for endpoints.  */
212     UX_ASSERT(!UX_DEVICE_CLASS_RNDIS_ENDPOINT_BUFFER_SIZE_CALC_OVERFLOW);
213     rndis -> ux_device_class_rndis_endpoint_buffer = _ux_utility_memory_allocate(
214                             UX_NO_ALIGN,UX_CACHE_SAFE_MEMORY,
215                             UX_DEVICE_CLASS_RNDIS_ENDPOINT_BUFFER_SIZE);
216     if (rndis -> ux_device_class_rndis_endpoint_buffer == UX_NULL)
217         status = UX_MEMORY_INSUFFICIENT;
218 #else
219     status = UX_SUCCESS;
220 #endif
221 
222     /* Create a mutex to protect the RNDIS thread and the application messing up the transmit queue.  */
223     if (status == UX_SUCCESS)
224     {
225         status =  _ux_utility_mutex_create(&rndis -> ux_slave_class_rndis_mutex, "ux_slave_class_rndis_mutex");
226         if (status != UX_SUCCESS)
227             status = UX_MUTEX_ERROR;
228     }
229 
230     /* Allocate some memory for the interrupt thread stack. */
231     if (status == UX_SUCCESS)
232     {
233         rndis -> ux_slave_class_rndis_interrupt_thread_stack =
234                 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE);
235 
236         /* Check for successful allocation.  */
237         if (rndis -> ux_slave_class_rndis_interrupt_thread_stack  == UX_NULL)
238 
239             /* Set status to memory insufficient.  */
240             status = UX_MEMORY_INSUFFICIENT;
241     }
242 
243     /* Interrupt endpoint treatment needs to be running in a different thread. So start
244        a new thread. We pass a pointer to the rndis instance to the new thread.  This thread
245        does not start until we have a instance of the class. */
246     if (status == UX_SUCCESS)
247     {
248         status =  _ux_device_thread_create(&rndis -> ux_slave_class_rndis_interrupt_thread , "ux_slave_class_rndis_interrupt_thread",
249                     _ux_device_class_rndis_interrupt_thread,
250                     (ULONG) (ALIGN_TYPE) class_ptr, (VOID *) rndis -> ux_slave_class_rndis_interrupt_thread_stack ,
251                     UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS,
252                     UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);
253 
254         /* Check the creation of this thread.  */
255         if (status != UX_SUCCESS)
256             status = UX_THREAD_ERROR;
257     }
258 
259     UX_THREAD_EXTENSION_PTR_SET(&(rndis -> ux_slave_class_rndis_interrupt_thread), class_ptr)
260 
261     /* Allocate some memory for the bulk out thread stack. */
262     if (status == UX_SUCCESS)
263     {
264         rndis -> ux_slave_class_rndis_bulkout_thread_stack =
265                 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE);
266 
267         /* Check for successful allocation.  */
268         if (rndis -> ux_slave_class_rndis_bulkout_thread_stack  == UX_NULL)
269             status = UX_MEMORY_INSUFFICIENT;
270     }
271 
272     /* Bulk endpoint treatment needs to be running in a different thread. So start
273        a new thread. We pass a pointer to the rndis instance to the new thread.  This thread
274        does not start until we have a instance of the class. */
275     if (status == UX_SUCCESS)
276     {
277         status =  _ux_device_thread_create(&rndis -> ux_slave_class_rndis_bulkout_thread , "ux_slave_class_rndis_bulkout_thread",
278                     _ux_device_class_rndis_bulkout_thread,
279                     (ULONG) (ALIGN_TYPE) class_ptr, (VOID *) rndis -> ux_slave_class_rndis_bulkout_thread_stack ,
280                     UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS,
281                     UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);
282 
283         /* Check the creation of this thread.  */
284         if (status != UX_SUCCESS)
285             status = UX_THREAD_ERROR;
286     }
287 
288     UX_THREAD_EXTENSION_PTR_SET(&(rndis -> ux_slave_class_rndis_bulkout_thread), class_ptr)
289 
290     /* Allocate some memory for the bulk in thread stack. */
291     if (status == UX_SUCCESS)
292     {
293         rndis -> ux_slave_class_rndis_bulkin_thread_stack =
294                 _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE);
295 
296         /* Check for successful allocation.  */
297         if (rndis -> ux_slave_class_rndis_bulkin_thread_stack  == UX_NULL)
298             status = UX_MEMORY_INSUFFICIENT;
299     }
300 
301     /* Bulk endpoint treatment needs to be running in a different thread. So start
302        a new thread. We pass a pointer to the rndis instance to the new thread.  This thread
303        does not start until we have a instance of the class. */
304     if (status == UX_SUCCESS)
305     {
306         status =  _ux_device_thread_create(&rndis -> ux_slave_class_rndis_bulkin_thread , "ux_slave_class_rndis_bulkin_thread",
307                     _ux_device_class_rndis_bulkin_thread,
308                     (ULONG) (ALIGN_TYPE) class_ptr, (VOID *) rndis -> ux_slave_class_rndis_bulkin_thread_stack ,
309                     UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS,
310                     UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_DONT_START);
311 
312         /* Check the creation of this thread.  */
313         if (status != UX_SUCCESS)
314             status = UX_THREAD_ERROR;
315     }
316 
317     UX_THREAD_EXTENSION_PTR_SET(&(rndis -> ux_slave_class_rndis_bulkin_thread), class_ptr)
318 
319     /* Create a event flag group for the rndis class to synchronize with the event interrupt thread.  */
320     if (status == UX_SUCCESS)
321     {
322         status =  _ux_utility_event_flags_create(&rndis -> ux_slave_class_rndis_event_flags_group, "ux_device_class_rndis_event_flag");
323 
324         /* Check status.  */
325         if (status != UX_SUCCESS)
326             status = UX_EVENT_ERROR;
327     }
328 
329     /* Create a semaphore for protecting the driver entry.  */
330     if (status == UX_SUCCESS)
331     {
332         status =  _ux_device_semaphore_create(&rndis -> ux_slave_class_rndis_semaphore, "ux_device_class_rndis_semaphore", 1);
333         if (status == UX_SUCCESS)
334 
335             /* Return success.  */
336             return(UX_SUCCESS);
337 
338         /* Semaphore creation error.  */
339         status = UX_SEMAPHORE_ERROR;
340     }
341 
342     /* Failed! Free up resources. */
343 
344     /* Delete semaphore for protecting the driver entry.  */
345     if (rndis -> ux_slave_class_rndis_semaphore.tx_semaphore_id != 0)
346         _ux_device_semaphore_delete(&rndis -> ux_slave_class_rndis_semaphore);
347 
348     /* Delete rndis -> ux_slave_class_rndis_event_flags_group.  */
349     if (rndis -> ux_slave_class_rndis_event_flags_group.tx_event_flags_group_id != 0)
350         _ux_utility_event_flags_delete(&rndis -> ux_slave_class_rndis_event_flags_group);
351 
352     /* Delete rndis -> ux_slave_class_rndis_bulkin_thread.  */
353     if (rndis -> ux_slave_class_rndis_bulkin_thread.tx_thread_id != 0)
354         _ux_device_thread_delete(&rndis -> ux_slave_class_rndis_bulkin_thread);
355 
356     /* Free rndis -> ux_slave_class_rndis_bulkin_thread_stack.  */
357     if (rndis -> ux_slave_class_rndis_bulkin_thread_stack)
358         _ux_utility_memory_free(rndis -> ux_slave_class_rndis_bulkin_thread_stack);
359 
360     /* Delete rndis -> ux_slave_class_rndis_bulkout_thread.  */
361     if (rndis -> ux_slave_class_rndis_bulkout_thread.tx_thread_id != 0)
362         _ux_device_thread_delete(&rndis -> ux_slave_class_rndis_bulkout_thread);
363 
364     /* Free rndis -> ux_slave_class_rndis_bulkout_thread_stack.  */
365     if (rndis -> ux_slave_class_rndis_bulkout_thread_stack)
366         _ux_utility_memory_free(rndis -> ux_slave_class_rndis_bulkout_thread_stack);
367 
368     /* Delete rndis -> ux_slave_class_rndis_interrupt_thread.  */
369     if (rndis -> ux_slave_class_rndis_interrupt_thread.tx_thread_id != 0)
370         _ux_device_thread_delete(&rndis -> ux_slave_class_rndis_interrupt_thread);
371 
372     /* Free rndis -> ux_slave_class_rndis_interrupt_thread_stack.  */
373     if (rndis -> ux_slave_class_rndis_interrupt_thread_stack)
374         _ux_utility_memory_free(rndis -> ux_slave_class_rndis_interrupt_thread_stack);
375 
376     /* Delete rndis -> ux_slave_class_rndis_mutex.  */
377     if (rndis -> ux_slave_class_rndis_mutex.tx_mutex_id != 0)
378         _ux_device_mutex_delete(&rndis -> ux_slave_class_rndis_mutex);
379 
380 #if UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1
381     if (rndis -> ux_device_class_rndis_endpoint_buffer != UX_NULL)
382         _ux_utility_memory_free(rndis -> ux_device_class_rndis_endpoint_buffer);
383 #endif
384 
385     /* Free memory for rndis instance.  */
386     _ux_utility_memory_free(rndis);
387 
388     /* Return completion status.  */
389     return(status);
390 #endif
391 }
392