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 ACM 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_acm.h"
29 #include "ux_host_stack.h"
30
31
32 /**************************************************************************/
33 /* */
34 /* FUNCTION RELEASE */
35 /* */
36 /* _ux_host_class_cdc_acm_activate PORTABLE C */
37 /* 6.3.0 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function creates the ACM instance, configure the device ... */
45 /* */
46 /* INPUT */
47 /* */
48 /* command ACM class command pointer */
49 /* */
50 /* OUTPUT */
51 /* */
52 /* Completion Status */
53 /* */
54 /* CALLS */
55 /* */
56 /* _ux_host_class_cdc_acm_configure Configure cdc_acm class */
57 /* _ux_host_class_cdc_acm_endpoints_get Get endpoints of cdc_acm */
58 /* _ux_host_class_cdc_acm_ioctl IOCTL function for ACM */
59 /* _ux_host_stack_class_instance_destroy Destroy the class instance */
60 /* _ux_host_stack_endpoint_transfer_abort Abort transfer */
61 /* _ux_utility_memory_allocate Allocate memory block */
62 /* _ux_utility_memory_free Free memory */
63 /* _ux_host_semaphore_create Create cdc_acm semaphore */
64 /* _ux_host_semaphore_delete Delete semaphore */
65 /* _ux_utility_delay_ms Delay */
66 /* */
67 /* CALLED BY */
68 /* */
69 /* _ux_host_class_cdc_acm_entry Entry of cdc_acm class */
70 /* _ux_utility_delay_ms Delay ms */
71 /* */
72 /* RELEASE HISTORY */
73 /* */
74 /* DATE NAME DESCRIPTION */
75 /* */
76 /* 05-19-2020 Chaoqiong Xiao Initial Version 6.0 */
77 /* 09-30-2020 Chaoqiong Xiao Modified comment(s), */
78 /* resulting in version 6.1 */
79 /* 01-31-2022 Chaoqiong Xiao Modified comment(s), */
80 /* added standalone support, */
81 /* used defined line coding */
82 /* instead of magic number, */
83 /* resulting in version 6.1.10 */
84 /* 07-29-2022 Chaoqiong Xiao Modified comment(s), */
85 /* improved error handling, */
86 /* fixed parameter/variable */
87 /* names conflict C++ keyword, */
88 /* resulting in version 6.1.12 */
89 /* 10-31-2023 Chaoqiong Xiao Modified comment(s), */
90 /* improved control searching, */
91 /* resulting in version 6.3.0 */
92 /* */
93 /**************************************************************************/
_ux_host_class_cdc_acm_activate(UX_HOST_CLASS_COMMAND * command)94 UINT _ux_host_class_cdc_acm_activate(UX_HOST_CLASS_COMMAND *command)
95 {
96
97 UX_INTERFACE *interface_ptr;
98 UX_HOST_CLASS_CDC_ACM *cdc_acm;
99 UINT status;
100 #if defined(UX_HOST_STANDALONE)
101 UX_HOST_CLASS *cdc_acm_class;
102 UX_HOST_CLASS_CDC_ACM *cdc_acm_inst;
103 UX_ENDPOINT *control_endpoint;
104 UX_TRANSFER *transfer_request;
105 ULONG descriptors_length;
106 #else
107 UX_HOST_CLASS_CDC_ACM_LINE_CODING line_coding;
108 UX_HOST_CLASS_CDC_ACM_LINE_STATE line_state;
109 #endif
110
111 /* The CDC ACM class is always activated by the interface descriptor and not the
112 device descriptor. */
113 interface_ptr = (UX_INTERFACE *) command -> ux_host_class_command_container;
114
115 /* Obtain memory for this class instance. */
116 cdc_acm = _ux_utility_memory_allocate(UX_NO_ALIGN, UX_CACHE_SAFE_MEMORY, sizeof(UX_HOST_CLASS_CDC_ACM));
117
118 /* Instance creation fail. */
119 if (cdc_acm == UX_NULL)
120
121 /* Memory allocation fail. */
122 return(UX_MEMORY_INSUFFICIENT);
123
124 #if !defined(UX_HOST_STANDALONE)
125
126 /* Create the semaphore to protect 2 threads from accessing the same acm instance. */
127 status = _ux_host_semaphore_create(&cdc_acm -> ux_host_class_cdc_acm_semaphore, "ux_host_class_cdc_acm_semaphore", 1);
128 if (status != UX_SUCCESS)
129 {
130
131 /* Free instance memory. */
132 _ux_utility_memory_free(cdc_acm);
133
134 /* Semaphore creation error. */
135 return(UX_SEMAPHORE_ERROR);
136 }
137 #endif
138
139 /* Store the class container into this instance. */
140 cdc_acm -> ux_host_class_cdc_acm_class = command -> ux_host_class_command_class_ptr;
141
142 /* Store the interface container into the cdc_acm class instance. */
143 cdc_acm -> ux_host_class_cdc_acm_interface = interface_ptr;
144
145 /* Store the device container into the cdc_acm class instance. */
146 cdc_acm -> ux_host_class_cdc_acm_device = interface_ptr -> ux_interface_configuration -> ux_configuration_device;
147
148 /* This instance of the device must also be stored in the interface container. */
149 interface_ptr -> ux_interface_class_instance = (VOID *) cdc_acm;
150
151 /* Create this class instance. */
152 _ux_host_stack_class_instance_create(cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm);
153
154 #if defined(UX_HOST_STANDALONE)
155
156 /* Get the cdc_acm endpoint(s). Depending on the interface type, we will need to search for
157 Bulk Out and Bulk In endpoints and the optional interrupt endpoint. */
158 status = _ux_host_class_cdc_acm_endpoints_get(cdc_acm);
159 if (status == UX_SUCCESS)
160 {
161
162 /* Mark the cdc_acm as mounting. */
163 cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_MOUNTING;
164
165 /* If we have the Control Class, we process default setup command sequence. */
166 if (interface_ptr -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS)
167 {
168
169 /* Get descriptors to see capabilities. */
170
171 /* Get default control transfer. */
172 control_endpoint = &cdc_acm -> ux_host_class_cdc_acm_device -> ux_device_control_endpoint;
173 transfer_request = &control_endpoint -> ux_endpoint_transfer_request;
174
175 /* Allocate memory for the descriptors. */
176 descriptors_length = interface_ptr -> ux_interface_configuration ->
177 ux_configuration_descriptor.wTotalLength;
178 cdc_acm -> ux_host_class_cdc_acm_allocated =
179 _ux_utility_memory_allocate(UX_SAFE_ALIGN, UX_CACHE_SAFE_MEMORY,
180 descriptors_length);
181 if (cdc_acm -> ux_host_class_cdc_acm_allocated != UX_NULL)
182 {
183
184 transfer_request -> ux_transfer_request_data_pointer =
185 cdc_acm -> ux_host_class_cdc_acm_allocated;
186
187 /* Create transfer for GET_DESCRIPTOR. */
188 transfer_request -> ux_transfer_request_requested_length = descriptors_length;
189 transfer_request -> ux_transfer_request_function = UX_GET_DESCRIPTOR;
190 transfer_request -> ux_transfer_request_type = UX_REQUEST_IN | UX_REQUEST_TYPE_STANDARD | UX_REQUEST_TARGET_DEVICE;
191 transfer_request -> ux_transfer_request_value = UX_CONFIGURATION_DESCRIPTOR_ITEM << 8;
192 transfer_request -> ux_transfer_request_index = 0;
193 UX_TRANSFER_STATE_RESET(transfer_request);
194
195 /* Set state to wait and next is "next". */
196 cdc_acm -> ux_host_class_cdc_acm_cmd_state = UX_STATE_WAIT;
197 cdc_acm -> ux_host_class_cdc_acm_next_state = UX_STATE_NEXT;
198
199 /* ACTIVATE_WAIT will be processed to finish next steps. */
200 return(UX_SUCCESS);
201 }
202 else
203 status = UX_MEMORY_INSUFFICIENT;
204 }
205 else
206 {
207
208 /* We scan CDC ACM instances to find the master instance. */
209 /* Get class. */
210 cdc_acm_class = cdc_acm -> ux_host_class_cdc_acm_class;
211
212 /* Get first instance linked to the class. */
213 cdc_acm_inst = (UX_HOST_CLASS_CDC_ACM *)cdc_acm_class -> ux_host_class_first_instance;
214
215 /* Scan all instances. */
216 while(cdc_acm_inst)
217 {
218
219 /* If this data interface is on the same device and inside the associate list, link it. */
220 if ((cdc_acm_inst -> ux_host_class_cdc_acm_device ==
221 cdc_acm -> ux_host_class_cdc_acm_device) &&
222 (cdc_acm_inst -> ux_host_class_cdc_acm_interfaces_bitmap &
223 (1ul << interface_ptr -> ux_interface_descriptor.bInterfaceNumber)))
224 {
225
226 /* Save control instance. */
227 cdc_acm -> ux_host_class_cdc_acm_control = cdc_acm_inst;
228 }
229
230 /* Next instance. */
231 cdc_acm_inst = cdc_acm_inst -> ux_host_class_cdc_acm_next_instance;
232 }
233
234 /* Mark the cdc_acm as live now. Both interfaces need to be live. */
235 cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_LIVE;
236
237 /* If all is fine and the device is mounted, we may need to inform the application
238 if a function has been programmed in the system structure. */
239 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
240 {
241
242 /* Call system change function. */
243 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm);
244 }
245
246 /* If trace is enabled, insert this event into the trace buffer. */
247 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_ACTIVATE, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
248
249 /* If trace is enabled, register this object. */
250 UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, cdc_acm, 0, 0, 0)
251
252 /* We are done success. */
253 return(UX_SUCCESS);
254 }
255 }
256
257 #else
258
259 /* Configure the cdc_acm. */
260 status = _ux_host_class_cdc_acm_configure(cdc_acm);
261
262 /* If we are done success, go on to get endpoints. */
263 if (status == UX_SUCCESS)
264
265 /* Get the cdc_acm endpoint(s). Depending on the interface type, we will need to search for
266 Bulk Out and Bulk In endpoints and the optional interrupt endpoint. */
267 status = _ux_host_class_cdc_acm_endpoints_get(cdc_acm);
268
269 /* If we are done success, go on to mount interface. */
270 if (status == UX_SUCCESS)
271 {
272 /* Mark the cdc_acm as mounting now. Both interfaces need to be mounting. */
273 cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_MOUNTING;
274
275 /* If we have the Control Class, we have to configure the speed, parity ... */
276 if (cdc_acm -> ux_host_class_cdc_acm_interface -> ux_interface_descriptor.bInterfaceClass == UX_HOST_CLASS_CDC_CONTROL_CLASS)
277 {
278
279 /* We need to wait for some device to settle. The Radicom USB Modem is an example of
280 these device who fail the first Set_Line_Coding command if sent too quickly.
281 The timing does not have to be precise so we use the thread sleep function.
282 The default sleep value is 1 seconds. */
283 _ux_utility_delay_ms(UX_HOST_CLASS_CDC_ACM_DEVICE_INIT_DELAY);
284
285 /* Do a GET_LINE_CODING first. */
286 status = _ux_host_class_cdc_acm_ioctl(cdc_acm, UX_HOST_CLASS_CDC_ACM_IOCTL_GET_LINE_CODING, (VOID *) &line_coding);
287
288 /* If we are done success, go on. */
289 if (status == UX_SUCCESS)
290 {
291
292 /* Set the default values to the device, first line coding. */
293 line_coding.ux_host_class_cdc_acm_line_coding_dter = UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_RATE;
294 line_coding.ux_host_class_cdc_acm_line_coding_stop_bit = UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_STOP_BIT;
295 line_coding.ux_host_class_cdc_acm_line_coding_parity = UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_PARITY;
296 line_coding.ux_host_class_cdc_acm_line_coding_data_bits = UX_HOST_CLASS_CDC_ACM_LINE_CODING_DEFAULT_DATA_BIT;
297 status = _ux_host_class_cdc_acm_ioctl(cdc_acm, UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_CODING, (VOID *) &line_coding);
298 }
299
300 /* If we are done success, go on. */
301 if (status == UX_SUCCESS)
302 {
303
304 /* Set the default values to the device, line state. */
305 line_state.ux_host_class_cdc_acm_line_state_rts = 1;
306 line_state.ux_host_class_cdc_acm_line_state_dtr = 1;
307 status = _ux_host_class_cdc_acm_ioctl(cdc_acm, UX_HOST_CLASS_CDC_ACM_IOCTL_SET_LINE_STATE, (VOID *) &line_state);
308 }
309
310 /* If we are done success, go on. */
311 if (status == UX_SUCCESS)
312 {
313
314 /* Get the capabilities of the device. We need to know if the commands are multiplexed over the comm
315 interface or the data interface. */
316 status = _ux_host_class_cdc_acm_capabilities_get(cdc_acm);
317 }
318 }
319
320 /* If we are done success, go on. */
321 if (status == UX_SUCCESS)
322 {
323 /* Mark the cdc_acm as live now. Both interfaces need to be live. */
324 cdc_acm -> ux_host_class_cdc_acm_state = UX_HOST_CLASS_INSTANCE_LIVE;
325
326 /* If all is fine and the device is mounted, we may need to inform the application
327 if a function has been programmed in the system structure. */
328 if (_ux_system_host -> ux_system_host_change_function != UX_NULL)
329 {
330
331 /* Call system change function. */
332 _ux_system_host -> ux_system_host_change_function(UX_DEVICE_INSERTION, cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm);
333 }
334
335 /* If trace is enabled, insert this event into the trace buffer. */
336 UX_TRACE_IN_LINE_INSERT(UX_TRACE_HOST_CLASS_CDC_ACM_ACTIVATE, cdc_acm, 0, 0, 0, UX_TRACE_HOST_CLASS_EVENTS, 0, 0)
337
338 /* If trace is enabled, register this object. */
339 UX_TRACE_OBJECT_REGISTER(UX_TRACE_HOST_OBJECT_TYPE_INTERFACE, cdc_acm, 0, 0, 0)
340
341 /* We are done success. */
342 return(UX_SUCCESS);
343 }
344 }
345 #endif
346
347 /* On error case, it's possible data buffer allocated for interrupt endpoint and transfer started, stop and free it. */
348 if (cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint &&
349 cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer)
350 {
351
352 /* The first transfer request has already been initiated. Abort it. */
353 _ux_host_stack_endpoint_transfer_abort(cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint);
354
355 /* Free the memory for the data pointer. */
356 _ux_utility_memory_free(cdc_acm -> ux_host_class_cdc_acm_interrupt_endpoint -> ux_endpoint_transfer_request.ux_transfer_request_data_pointer);
357 }
358
359 /* Destroy the instance. */
360 _ux_host_stack_class_instance_destroy(cdc_acm -> ux_host_class_cdc_acm_class, (VOID *) cdc_acm);
361
362 #if !defined(UX_HOST_STANDALONE)
363
364 /* Destroy the semaphore. */
365 _ux_host_semaphore_delete(&cdc_acm -> ux_host_class_cdc_acm_semaphore);
366 #endif
367
368 /* Unmount instance. */
369 interface_ptr -> ux_interface_class_instance = UX_NULL;
370
371 /* Free instance. */
372 _ux_utility_memory_free(cdc_acm);
373
374 return(status);
375 }
376
377