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