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 CCID 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_ccid.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_ccid_thread_entry PORTABLE C */
37 /* 6.2.1 */
38 /* AUTHOR */
39 /* */
40 /* Chaoqiong Xiao, Microsoft Corporation */
41 /* */
42 /* DESCRIPTION */
43 /* */
44 /* This function is the thread of the CCID bulk out endpoint. It */
45 /* is waiting for the host to send message on the bulk out endpoint to */
46 /* the device. */
47 /* */
48 /* INPUT */
49 /* */
50 /* ccid_inst Address of ccid class */
51 /* container (32-bit) */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* None */
56 /* */
57 /* CALLS */
58 /* */
59 /* _ux_device_stack_transfer_request Request transfer */
60 /* */
61 /* CALLED BY */
62 /* */
63 /* ThreadX */
64 /* */
65 /* RELEASE HISTORY */
66 /* */
67 /* DATE NAME DESCRIPTION */
68 /* */
69 /* 04-25-2022 Chaoqiong Xiao Initial Version 6.1.11 */
70 /* 03-08-2023 Chaoqiong Xiao Modified comment(s), */
71 /* added standalone support, */
72 /* resulting in version 6.2.1 */
73 /* */
74 /**************************************************************************/
_ux_device_class_ccid_thread_entry(ULONG ccid_inst)75 VOID _ux_device_class_ccid_thread_entry(ULONG ccid_inst)
76 {
77
78 UX_SLAVE_DEVICE *device;
79 UX_DEVICE_CLASS_CCID *ccid;
80 UX_DEVICE_CLASS_CCID_SLOT *slot;
81 UX_DEVICE_CLASS_CCID_RUNNER *runner = UX_NULL;
82 UX_DEVICE_CLASS_CCID_PARAMETER *parameter;
83 UX_DEVICE_CLASS_CCID_MESSAGES messages;
84 UX_SLAVE_ENDPOINT *endpoint;
85 UX_SLAVE_TRANSFER *transfer_cmd;
86 UX_DEVICE_CLASS_CCID_MESSAGE_HEADER *cmd;
87 UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS_HEADER *rsp;
88 UX_DEVICE_CLASS_CCID_COMMAND_SETT *cmd_sett;
89 CHAR cmd_index;
90 UX_DEVICE_CLASS_CCID_HANDLE *handles;
91 INT i;
92 UINT status;
93
94 /* Cast properly the ccid instance. */
95 UX_THREAD_EXTENSION_PTR_GET(ccid, UX_DEVICE_CLASS_CCID, ccid_inst)
96
97 /* Get the pointer to the device. */
98 device = &_ux_system_slave -> ux_system_slave_device;
99
100 /* This thread runs forever but can be suspended or resumed by the user application. */
101 status = UX_SUCCESS;
102 while(1)
103 {
104
105 /* Error cases. */
106 if (status != UX_SUCCESS)
107 {
108
109 /* We need to suspend ourselves. We will be resumed by the
110 application if needed. */
111 _ux_utility_thread_suspend(&ccid -> ux_device_class_ccid_thread);
112 }
113
114 status = UX_ERROR;
115
116 /* Check device state. */
117 if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
118 continue;
119
120 /* Check endpoint. */
121 endpoint = ccid -> ux_device_class_ccid_endpoint_out;
122 if (endpoint == UX_NULL)
123 continue;
124 transfer_cmd = &endpoint -> ux_slave_endpoint_transfer_request;
125 endpoint = ccid -> ux_device_class_ccid_endpoint_in;
126 if (endpoint == UX_NULL)
127 continue;
128
129 /* Send the request to the device controller. */
130 parameter = &ccid -> ux_device_class_ccid_parameter;
131 status = _ux_device_stack_transfer_request(transfer_cmd,
132 parameter -> ux_device_class_ccid_max_transfer_length,
133 parameter -> ux_device_class_ccid_max_transfer_length);
134
135 /* Access to CCID command message header. */
136 cmd = (UX_DEVICE_CLASS_CCID_MESSAGE_HEADER *)
137 transfer_cmd -> ux_slave_transfer_request_data_pointer;
138
139 /* Check the completion code and message length. */
140 if ((status != UX_SUCCESS) ||
141 (transfer_cmd -> ux_slave_transfer_request_completion_code != UX_SUCCESS) ||
142 (transfer_cmd -> ux_slave_transfer_request_actual_length <
143 UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH) ||
144 (transfer_cmd -> ux_slave_transfer_request_actual_length >
145 parameter -> ux_device_class_ccid_max_transfer_length))
146 {
147
148 /* Re-check device state and try transfer again. */
149 status = UX_SUCCESS;
150 continue;
151 }
152
153 /* Get command setting. */
154 cmd_sett = (UX_DEVICE_CLASS_CCID_COMMAND_SETT *)_ux_device_class_ccid_command_sett;
155 for (cmd_index = 0; cmd_index < UX_DEVICE_CLASS_CCID_N_COMMANDS;)
156 {
157 if (cmd -> bMessageType ==
158 cmd_sett -> ux_device_class_ccid_command_sett_command_type)
159 break;
160
161 /* Next command setting. */
162 cmd_sett ++;
163 cmd_index ++;
164 }
165 handles = (UX_DEVICE_CLASS_CCID_HANDLE *)parameter -> ux_device_class_ccid_handles;
166
167 /* Lock global status resources. */
168 _ux_device_class_ccid_lock(ccid);
169
170 /* Initialize response. */
171 rsp = (UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS_HEADER *)
172 ccid -> ux_device_class_ccid_header;
173 _ux_utility_memory_set(rsp, 0, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH); /* Use case of memset is verified. */
174 rsp -> bMessageType = cmd_sett -> ux_device_class_ccid_command_sett_response_type;
175 rsp -> bSlot = cmd -> bSlot;
176 rsp -> bSeq = cmd -> bSeq;
177
178 /* Check command support (0,1,0). */
179 if (rsp -> bMessageType == 0 ||
180 handles[(INT)cmd_sett -> ux_device_class_ccid_command_sett_handle_index] == UX_NULL)
181 {
182
183 /* Response: command not supported (0,1,0). */
184 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(0, 1);
185 _ux_device_class_ccid_unlock(ccid);
186 _ux_device_class_ccid_response(ccid, (UCHAR *)rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH);
187 continue;
188 }
189
190 /* check Slot exist (2,1,5). */
191 if (cmd -> bSlot >= parameter -> ux_device_class_ccid_max_n_slots)
192 {
193
194 /* Response: Slot not exist. */
195 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(2, 1);
196 rsp -> bError = 5;
197 _ux_device_class_ccid_unlock(ccid);
198 _ux_device_class_ccid_response(ccid, (UCHAR *)rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH);
199 continue;
200 }
201
202 /* Get slot instance for later usage. */
203 slot = &ccid -> ux_device_class_ccid_slots[cmd -> bSlot];
204
205 /* Initialize response status from slot status. */
206 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
207 slot -> ux_device_class_ccid_slot_icc_status, 0);
208
209 /* Initialize response clock status. */
210 if (cmd_sett -> ux_device_class_ccid_command_sett_response_type == 0x81)
211 rsp -> bClockStatus = slot -> ux_device_class_ccid_slot_clock_status;
212
213 /* Abort command
214 - return slot status(OK) anyway
215 - clear aborting status
216 Aborting
217 - return slot status(ABORTED)
218 Other command (except SetDataRateAndClockFrequency)
219 - Check busy */
220
221 /* Abort command is handled differently. */
222 if (cmd -> bMessageType != UX_DEVICE_CLASS_CCID_PC_TO_RDR_ABORT ||
223 !slot -> ux_device_class_ccid_slot_aborting)
224 {
225
226 /* Check if slot is idle. */
227 if ((signed char)slot -> ux_device_class_ccid_slot_runner < 0)
228 {
229
230 /* Slot is idle, check if free runner available. */
231 if (ccid -> ux_device_class_ccid_n_busy <
232 parameter -> ux_device_class_ccid_max_n_busy_slots)
233 {
234
235 /* Get a free runner for the command. */
236 runner = ccid -> ux_device_class_ccid_runners;
237 for(i = 0;
238 i < parameter -> ux_device_class_ccid_max_n_busy_slots;
239 i ++)
240 {
241
242 /* Check if runner is free. */
243 if ((signed char)runner -> ux_device_class_ccid_runner_slot < 0)
244 break;
245
246 /* Check next runner. */
247 runner ++;
248 }
249
250 /* It's not possible no runner found here, just execute runner. */
251
252 /* Runner is busy now. */
253 runner -> ux_device_class_ccid_runner_slot = (CHAR)cmd->bSlot;
254 runner -> ux_device_class_ccid_runner_command_index = cmd_index;
255 ccid -> ux_device_class_ccid_n_busy ++;
256 slot -> ux_device_class_ccid_slot_runner = (CHAR)i;
257
258 /* Create a copy of command and response header. */
259 _ux_utility_memory_copy(runner -> ux_device_class_ccid_runner_command,
260 cmd,
261 transfer_cmd -> ux_slave_transfer_request_actual_length); /* Use case of memcpy is verified. */
262 _ux_utility_memory_copy(runner -> ux_device_class_ccid_runner_response,
263 rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH); /* Use case of memcpy is verified. */
264
265 /* Pre-process of command done. */
266 _ux_device_class_ccid_unlock(ccid);
267
268 /* Signal event to runner thread. */
269 _ux_utility_thread_resume(&runner -> ux_device_class_ccid_runner_thread);
270 _ux_device_event_flags_set(&ccid -> ux_device_class_ccid_events,
271 1u << i, UX_OR);
272 continue;
273 }
274
275 /* We are here if there is no runner available. */
276 /* It's busy! */
277 }
278
279 /* Response: Slot Status(busy). */
280 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
281 slot -> ux_device_class_ccid_slot_icc_status, 1);
282 rsp -> bError = UX_DEVICE_CLASS_CCID_CMD_SLOT_BUSY;
283 _ux_device_class_ccid_unlock(ccid);
284 _ux_device_class_ccid_response(ccid, (UCHAR *)rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH);
285 continue;
286 }
287
288 /* We are here when we see Abort command, or aborting.
289 - Abort command : slot status (ok/fail)
290 - Aborting : slot status (CMD_ABORTED)
291 */
292
293 /* Abort command. */
294 if (cmd -> bMessageType == UX_DEVICE_CLASS_CCID_PC_TO_RDR_ABORT)
295 {
296
297 /* Check sequence. */
298 if (cmd -> bSeq != slot -> ux_device_class_ccid_slot_aborting_seq)
299 {
300
301 /* Response: sequence error. */
302 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
303 slot -> ux_device_class_ccid_slot_icc_status, 1);
304 rsp -> bError = 6;
305 }
306 else
307 {
308
309 /* Aborting. */
310 if (slot -> ux_device_class_ccid_slot_aborting)
311 {
312
313 /* Call abort handle. */
314 messages.ux_device_class_ccid_messages_pc_to_rdr = (VOID *)cmd;
315 messages.ux_device_class_ccid_messages_rdr_to_pc = (VOID *)rsp;
316 messages.ux_device_class_ccid_messages_rdr_to_pc_length = 0;
317 parameter -> ux_device_class_ccid_handles ->
318 ux_device_class_ccid_handles_abort(cmd -> bSlot, &messages);
319
320 /* Status(OK) */
321 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
322 slot -> ux_device_class_ccid_slot_icc_status, 0);
323 }
324 else
325 {
326
327 /* Status(CMD_NOT_ABORTED)? */
328 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
329 slot -> ux_device_class_ccid_slot_icc_status, 1);
330 rsp -> bError = UX_DEVICE_CLASS_CCID_CMD_SLOT_BUSY;
331 }
332 }
333
334 /* Free runner. */
335 if ((signed char)slot -> ux_device_class_ccid_slot_runner >= 0)
336 {
337 runner = ccid -> ux_device_class_ccid_runners + slot -> ux_device_class_ccid_slot_runner;
338
339 runner -> ux_device_class_ccid_runner_slot = -1;
340
341 ccid -> ux_device_class_ccid_n_busy --;
342
343 /* Clear slot busy and aborting. */
344 slot -> ux_device_class_ccid_slot_runner = -1;
345 slot -> ux_device_class_ccid_slot_aborting = UX_FALSE;
346 }
347
348 _ux_device_class_ccid_unlock(ccid);
349
350 /* Send response any way. */
351 _ux_device_class_ccid_response(ccid, (UCHAR *)rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH);
352 continue;
353 }
354
355 /* Aborting. */
356
357 /* Response: Slot Status(aborted). */
358 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
359 slot -> ux_device_class_ccid_slot_icc_status, 1);
360 rsp -> bError = UX_DEVICE_CLASS_CCID_CMD_ABORTED;
361
362 _ux_device_class_ccid_unlock(ccid);
363
364 _ux_device_class_ccid_response(ccid, (UCHAR *)rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH);
365 }
366 }
367 #endif
368