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