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