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 static inline UINT _ux_device_class_ccid_cmd_task(UX_DEVICE_CLASS_CCID *ccid);
34 static inline UINT _ux_device_class_ccid_rsp_task(UX_DEVICE_CLASS_CCID *ccid);
35 
36 
37 /**************************************************************************/
38 /*                                                                        */
39 /*  FUNCTION                                               RELEASE        */
40 /*                                                                        */
41 /*    _ux_device_class_ccid_tasks_run                     PORTABLE C      */
42 /*                                                           6.2.1        */
43 /*  AUTHOR                                                                */
44 /*                                                                        */
45 /*    Chaoqiong Xiao, Microsoft Corporation                               */
46 /*                                                                        */
47 /*  DESCRIPTION                                                           */
48 /*                                                                        */
49 /*    This function is the background task of the CCID.                   */
50 /*                                                                        */
51 /*    It's for standalone mode.                                           */
52 /*                                                                        */
53 /*  INPUT                                                                 */
54 /*                                                                        */
55 /*    instance                              Pointer to CCID class         */
56 /*                                                                        */
57 /*  OUTPUT                                                                */
58 /*                                                                        */
59 /*    State machine status                                                */
60 /*    UX_STATE_EXIT                         Device not configured         */
61 /*    UX_STATE_IDLE                         No streaming transfer running */
62 /*    UX_STATE_WAIT                         Streaming transfer running    */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    ux_device_class_ccid_notify_task_run Run interrupt notify task      */
67 /*    ux_device_class_ccid_runner_task_run Run slot command runner task   */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    USBX Device Stack                                                   */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  03-08-2023     Chaoqiong Xiao           Initial Version 6.2.1         */
78 /*                                                                        */
79 /**************************************************************************/
_ux_device_class_ccid_tasks_run(VOID * instance)80 UINT _ux_device_class_ccid_tasks_run(VOID *instance)
81 {
82 UX_SLAVE_DEVICE                 *device;
83 UX_DEVICE_CLASS_CCID            *ccid;
84 ULONG                           run_count = 0;
85 
86 
87     /* Get ccid instance.  */
88     ccid = (UX_DEVICE_CLASS_CCID *) instance;
89 
90     /* Get the pointer to the device.  */
91     device =  &_ux_system_slave -> ux_system_slave_device;
92 
93     /* Check if the device is configured.  */
94     if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
95         return(UX_STATE_EXIT);
96 
97     /* Bulk OUT command process.  */
98     if (_ux_device_class_ccid_cmd_task(ccid) != UX_STATE_IDLE)
99         run_count ++;
100 
101     /* Bulk IN response process.  */
102     if (_ux_device_class_ccid_rsp_task(ccid) != UX_STATE_IDLE)
103         run_count ++;
104 
105     /* Interrupt IN notification process.  */
106     if (_ux_device_class_ccid_notify_task_run(ccid) != UX_STATE_IDLE)
107         run_count ++;
108 
109     /* Runner tasks process.  */
110     if (_ux_device_class_ccid_runner_task_run(ccid) != UX_STATE_IDLE)
111         run_count ++;
112 
113     return((run_count > 0) ? UX_STATE_WAIT : UX_STATE_IDLE);
114 }
_ux_device_class_ccid_cmd_task(UX_DEVICE_CLASS_CCID * ccid)115 static inline UINT _ux_device_class_ccid_cmd_task(UX_DEVICE_CLASS_CCID *ccid)
116 {
117 UX_INTERRUPT_SAVE_AREA
118 INT                                                 immediate_state = UX_TRUE;
119 UINT                                                status;
120 UX_DEVICE_CLASS_CCID_SLOT                           *slot;
121 UX_DEVICE_CLASS_CCID_RUNNER                         *runner = UX_NULL;
122 UX_DEVICE_CLASS_CCID_PARAMETER                      *parameter;
123 UX_DEVICE_CLASS_CCID_MESSAGES                       messages;
124 UX_SLAVE_ENDPOINT                                   *endpoint;
125 UX_SLAVE_TRANSFER                                   *transfer_cmd;
126 UX_DEVICE_CLASS_CCID_MESSAGE_HEADER                 *cmd;
127 UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS_HEADER   *rsp;
128 UX_DEVICE_CLASS_CCID_COMMAND_SETT                   *cmd_sett;
129 CHAR                                                cmd_index;
130 UX_DEVICE_CLASS_CCID_HANDLE                         *handles;
131 
132 
133     /* Check endpoint.  */
134     endpoint = ccid -> ux_device_class_ccid_endpoint_out;
135     if (endpoint == UX_NULL)
136     {
137         ccid -> ux_device_class_ccid_cmd_state = UX_STATE_RESET;
138         return(UX_STATE_EXIT);
139     }
140     transfer_cmd = &endpoint -> ux_slave_endpoint_transfer_request;
141 
142     /* Get running settings.  */
143     parameter = &ccid -> ux_device_class_ccid_parameter;
144 
145     /* Wait Bulk-OUT command.  */
146     while(immediate_state)
147     {
148 
149         /* Process state.  */
150         switch(ccid -> ux_device_class_ccid_cmd_state)
151         {
152         case UX_DEVICE_CLASS_CCID_CMD_START:
153 
154             /* Prepare transfer, next: wait.  */
155             ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_WAIT;
156             UX_SLAVE_TRANSFER_STATE_RESET(transfer_cmd);
157 
158             /* Fall through.  */
159         case UX_DEVICE_CLASS_CCID_CMD_WAIT:
160             status = _ux_device_stack_transfer_run(transfer_cmd,
161                     parameter -> ux_device_class_ccid_max_transfer_length,
162                     parameter -> ux_device_class_ccid_max_transfer_length);
163 
164             /* Error case.  */
165             if (status < UX_STATE_NEXT)
166             {
167 
168                 ccid -> ux_device_class_ccid_cmd_state = UX_STATE_RESET;
169                 return(UX_STATE_ERROR);
170             }
171 
172             /* Success case.  */
173             if (status == UX_STATE_NEXT)
174             {
175 
176                 /* Check transfer results.  */
177                 if ((transfer_cmd -> ux_slave_transfer_request_completion_code != UX_SUCCESS) ||
178                     (transfer_cmd -> ux_slave_transfer_request_actual_length <
179                         UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH) ||
180                     (transfer_cmd -> ux_slave_transfer_request_actual_length >
181                         parameter -> ux_device_class_ccid_max_transfer_length))
182                 {
183 
184                     ccid -> ux_device_class_ccid_cmd_state = UX_STATE_RESET;
185                     return(UX_STATE_ERROR);
186                 }
187 
188                 /* Access to CCID command message header.  */
189                 cmd = (UX_DEVICE_CLASS_CCID_MESSAGE_HEADER *)
190                                 transfer_cmd -> ux_slave_transfer_request_data_pointer;
191 
192                 /* Get command setting index.  */
193                 cmd_sett = (UX_DEVICE_CLASS_CCID_COMMAND_SETT *)_ux_device_class_ccid_command_sett;
194                 for (cmd_index = 0; cmd_index < UX_DEVICE_CLASS_CCID_N_COMMANDS;)
195                 {
196                     if (cmd -> bMessageType ==
197                         cmd_sett -> ux_device_class_ccid_command_sett_command_type)
198                         break;
199 
200                     /* Next command setting.  */
201                     cmd_sett ++;
202                     cmd_index ++;
203                 }
204 
205                 /* Save command index for further actions.  */
206                 ccid -> ux_device_class_ccid_cmd_index = cmd_index;
207 
208                 /* Next: lock and update status.  */
209                 ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_LOCK;
210                 continue;
211             }
212 
213             /* Wait.  */
214             return(UX_STATE_WAIT);
215 
216         case UX_DEVICE_CLASS_CCID_CMD_LOCK:
217             UX_DISABLE
218             if (ccid -> ux_device_class_ccid_flags & UX_DEVICE_CLASS_CCID_FLAG_LOCK)
219             {
220                 UX_RESTORE
221                 return(UX_STATE_WAIT);
222             }
223             ccid -> ux_device_class_ccid_flags |= UX_DEVICE_CLASS_CCID_FLAG_LOCK;
224             UX_RESTORE
225 
226             /* Fall through.  */
227         case UX_DEVICE_CLASS_CCID_CMD_PROCESS:
228             cmd = (UX_DEVICE_CLASS_CCID_MESSAGE_HEADER *)
229                             transfer_cmd -> ux_slave_transfer_request_data_pointer;
230             cmd_index = ccid -> ux_device_class_ccid_cmd_index;
231             cmd_sett = (UX_DEVICE_CLASS_CCID_COMMAND_SETT *)&_ux_device_class_ccid_command_sett[(INT)cmd_index];
232             handles = (UX_DEVICE_CLASS_CCID_HANDLE *)parameter -> ux_device_class_ccid_handles;
233 
234             /* Initialize response.  */
235             rsp = (UX_DEVICE_CLASS_CCID_RDR_TO_PC_SLOT_STATUS_HEADER *)
236                                                 ccid -> ux_device_class_ccid_header;
237             _ux_utility_memory_set(rsp, 0, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH); /* Use case of memset is verified. */
238             rsp -> bMessageType = cmd_sett -> ux_device_class_ccid_command_sett_response_type;
239             rsp -> bSlot        = cmd -> bSlot;
240             rsp -> bSeq         = cmd -> bSeq;
241 
242             /* Check command support (0,1,0).  */
243             if (rsp -> bMessageType == 0 ||
244                 handles[(INT)cmd_sett -> ux_device_class_ccid_command_sett_handle_index] == UX_NULL)
245             {
246 
247                 /* Response: command not supported (0,1,0).  */
248                 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(0, 1);
249                 _ux_device_class_ccid_unlock(ccid);
250 
251                 /* Next: response.  */
252                 ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_RSP_START;
253                 return(UX_STATE_IDLE);
254             }
255 
256             /* check Slot exist (2,1,5).  */
257             if (cmd -> bSlot >= parameter -> ux_device_class_ccid_max_n_slots)
258             {
259 
260                 /* Response: Slot not exist.  */
261                 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(2, 1);
262                 rsp -> bError  = 5;
263                 _ux_device_class_ccid_unlock(ccid);
264 
265                 /* Next: response.  */
266                 ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_RSP_START;
267                 return(UX_STATE_IDLE);
268             }
269 
270             /* Get slot instance for later usage (optimized only 1 slot).  */
271             slot = ccid -> ux_device_class_ccid_slots;
272 
273             /* Initialize response status from slot status.  */
274             rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
275                                 slot -> ux_device_class_ccid_slot_icc_status, 0);
276 
277             /* Initialize response clock status.  */
278             if (cmd_sett -> ux_device_class_ccid_command_sett_response_type == 0x81)
279                 rsp -> bClockStatus = slot -> ux_device_class_ccid_slot_clock_status;
280 
281             /* Abort command
282                 - return slot status(OK) anyway
283                 - clear aborting status
284                Aborting
285                 - return slot status(ABORTED)
286                Other command (except SetDataRateAndClockFrequency)
287                 - Check busy  */
288 
289             /* Abort command is handled differently.  */
290             if (cmd -> bMessageType != UX_DEVICE_CLASS_CCID_PC_TO_RDR_ABORT ||
291                 !slot -> ux_device_class_ccid_slot_aborting)
292             {
293 
294                 /* Check if slot is idle (optimized one slot).  */
295                 runner = ccid -> ux_device_class_ccid_runners;
296                 if (ccid -> ux_device_class_ccid_n_busy == 0)
297                 {
298 
299                     /* It's not possible no runner found here, just execute runner.  */
300 
301                     /* Runner is busy now.  */
302                     runner -> ux_device_class_ccid_runner_slot = (CHAR)cmd->bSlot;
303                     runner -> ux_device_class_ccid_runner_command_index = cmd_index;
304                     ccid -> ux_device_class_ccid_n_busy ++;
305 
306                     /* Create a copy of command and response header.  */
307                     _ux_utility_memory_copy(runner -> ux_device_class_ccid_runner_command,
308                                             cmd,
309                                             transfer_cmd -> ux_slave_transfer_request_actual_length); /* Use case of memcpy is verified. */
310                     _ux_utility_memory_copy(runner -> ux_device_class_ccid_runner_response,
311                                             rsp, UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH); /* Use case of memcpy is verified. */
312 
313                     /* Pre-process of command done.  */
314                     _ux_device_class_ccid_unlock(ccid);
315 
316                     /* Update runner state to start.  */
317                     runner -> ux_device_class_ccid_runner_state = UX_DEVICE_CLASS_CCID_RUNNER_START;
318 
319                     /* Command processed, command is allowed when runner is executing.  */
320                     ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_START;
321                     return(UX_STATE_WAIT);
322                 }
323 
324                 /* Response: Slot Status(busy), optimized for 1 slot.  */
325                 rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
326                                 slot -> ux_device_class_ccid_slot_icc_status, 1);
327                 rsp -> bError = UX_DEVICE_CLASS_CCID_CMD_SLOT_BUSY;
328                 _ux_device_class_ccid_unlock(ccid);
329 
330                 /* Next: response (status busy).  */
331                 ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_RSP_START;
332                 continue;
333             }
334 
335             /* We are here when we see Abort command, or aborting.
336                 - Abort command : slot status (ok/fail)
337                 - Aborting : slot status (CMD_ABORTED)
338             */
339 
340             /* Abort command.  */
341             if (cmd -> bMessageType == UX_DEVICE_CLASS_CCID_PC_TO_RDR_ABORT)
342             {
343 
344                 /* Check sequence.  */
345                 if (cmd -> bSeq != slot -> ux_device_class_ccid_slot_aborting_seq)
346                 {
347 
348                     /* Response: sequence error.  */
349                     rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
350                                     slot -> ux_device_class_ccid_slot_icc_status, 1);
351                     rsp -> bError  = 6;
352                 }
353                 else
354                 {
355 
356                     /* Aborting.  */
357                     if (slot -> ux_device_class_ccid_slot_aborting)
358                     {
359 
360                         /* Call abort handle.  */
361                         messages.ux_device_class_ccid_messages_pc_to_rdr = (VOID *)cmd;
362                         messages.ux_device_class_ccid_messages_rdr_to_pc = (VOID *)rsp;
363                         messages.ux_device_class_ccid_messages_rdr_to_pc_length = 0;
364                         parameter -> ux_device_class_ccid_handles ->
365                                 ux_device_class_ccid_handles_abort(cmd -> bSlot, &messages);
366 
367                         /* Status(OK)  */
368                         rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
369                                         slot -> ux_device_class_ccid_slot_icc_status, 0);
370 
371                         /* Free runner (optimized only 1 slot).  */
372                         runner = ccid -> ux_device_class_ccid_runners;
373                         runner -> ux_device_class_ccid_runner_slot = -1;
374                         ccid -> ux_device_class_ccid_n_busy --;
375 
376                         /* Clear slot busy and aborting.  */
377                         slot -> ux_device_class_ccid_slot_runner = -1;
378                         slot -> ux_device_class_ccid_slot_aborting = UX_FALSE;
379                     }
380                     else
381                     {
382 
383                         /* Status(CMD_NOT_ABORTED)?  */
384                         rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
385                                         slot -> ux_device_class_ccid_slot_icc_status, 1);
386                         rsp -> bError  = UX_DEVICE_CLASS_CCID_CMD_SLOT_BUSY;
387                     }
388                 }
389 
390                 _ux_device_class_ccid_unlock(ccid);
391 
392                 /* Next: response.  */
393                 ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_RSP_START;
394                 return(UX_STATE_IDLE);
395             }
396 
397             /* Aborting.  */
398 
399             /* Response: Slot Status(aborted).  */
400             rsp -> bStatus = UX_DEVICE_CLASS_CCID_SLOT_STATUS(
401                                 slot -> ux_device_class_ccid_slot_icc_status, 1);
402             rsp -> bError = UX_DEVICE_CLASS_CCID_CMD_ABORTED;
403 
404             _ux_device_class_ccid_unlock(ccid);
405 
406             /* Next: response.  */
407             ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_RSP_START;
408 
409             /* Fall through.  */
410         case UX_DEVICE_CLASS_CCID_CMD_RSP_START:
411 
412             /* Wait until rsponse task is idle.  */
413             if (ccid -> ux_device_class_ccid_rsp_state != UX_DEVICE_CLASS_CCID_RSP_IDLE)
414                 return(UX_STATE_WAIT);
415 
416             /* Start response.  */
417             _ux_device_class_ccid_response(ccid,
418                                     ccid -> ux_device_class_ccid_header,
419                                     UX_DEVICE_CLASS_CCID_MESSAGE_HEADER_LENGTH);
420 
421             /* Command is idle and started after response sent.  */
422             ccid -> ux_device_class_ccid_flags |= UX_DEVICE_CLASS_CCID_FLAG_CMD_RSP;
423             ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_IDLE;
424 
425             /* Fall through.  */
426         case UX_DEVICE_CLASS_CCID_CMD_IDLE:
427             return(UX_STATE_IDLE);
428 
429         default:
430             break;
431         }
432 
433         /* Break the loop.  */
434         immediate_state = UX_FALSE;
435     }
436 
437     return(UX_STATE_WAIT);
438 }
_ux_device_class_ccid_rsp_task(UX_DEVICE_CLASS_CCID * ccid)439 static inline UINT _ux_device_class_ccid_rsp_task(UX_DEVICE_CLASS_CCID *ccid)
440 {
441 UX_INTERRUPT_SAVE_AREA
442 INT                                                 immediate_state = UX_TRUE;
443 UINT                                                status;
444 UX_SLAVE_ENDPOINT                                   *endpoint;
445 UX_SLAVE_TRANSFER                                   *transfer_rsp;
446 ULONG                                               length;
447 
448     /* Check endpoint.  */
449     endpoint = ccid -> ux_device_class_ccid_endpoint_in;
450     if (endpoint == UX_NULL)
451     {
452         ccid -> ux_device_class_ccid_cmd_state = UX_STATE_RESET;
453         return(UX_STATE_EXIT);
454     }
455     transfer_rsp = &endpoint -> ux_slave_endpoint_transfer_request;
456 
457     while(immediate_state)
458     {
459 
460         switch(ccid -> ux_device_class_ccid_rsp_state)
461         {
462 
463         case UX_DEVICE_CLASS_CCID_RSP_IDLE:
464             return(UX_STATE_IDLE);
465 
466         case UX_DEVICE_CLASS_CCID_RSP_START:
467             ccid -> ux_device_class_ccid_rsp_state = UX_DEVICE_CLASS_CCID_RSP_WAIT;
468 
469             /* Fall through.  */
470         case UX_DEVICE_CLASS_CCID_RSP_WAIT:
471 
472             /* Run bulk IN transfer.  */
473             length = transfer_rsp -> ux_slave_transfer_request_requested_length;
474             status = _ux_device_stack_transfer_run(transfer_rsp, length, length);
475 
476             /* Error/success case.  */
477             if (status <= UX_STATE_NEXT)
478             {
479 
480                 /* Transfer is done.  */
481 
482                 /* Check if it's command response or runner response.
483                    After runner response status needs update (optimized 1 slot).  */
484                 if (ccid -> ux_device_class_ccid_flags & UX_DEVICE_CLASS_CCID_FLAG_CMD_RSP)
485                 {
486                     ccid -> ux_device_class_ccid_flags &= ~UX_DEVICE_CLASS_CCID_FLAG_CMD_RSP;
487 
488                     /* CMD -RSP: done.  */
489                     ccid -> ux_device_class_ccid_rsp_state = UX_DEVICE_CLASS_CCID_RSP_DONE;
490                     continue;
491                 }
492 
493                 /* CMD - RUNNER - RSP : Update status.  */
494                 ccid -> ux_device_class_ccid_rsp_state = UX_DEVICE_CLASS_CCID_RSP_LOCK;
495                 continue;
496 
497             }
498 
499             /* Wait transfer.  */
500             return(UX_STATE_WAIT);
501 
502         case UX_DEVICE_CLASS_CCID_RSP_LOCK:
503             UX_DISABLE
504             if (ccid -> ux_device_class_ccid_flags & UX_DEVICE_CLASS_CCID_FLAG_LOCK)
505             {
506                 UX_RESTORE
507                 return(UX_STATE_WAIT);
508             }
509             ccid -> ux_device_class_ccid_flags |= UX_DEVICE_CLASS_CCID_FLAG_LOCK;
510             UX_RESTORE
511 
512             /* Fall through.  */
513         case UX_DEVICE_CLASS_CCID_RSP_UPDATE:
514 
515             /* Free runner and clear busy slot (optimized 1 slot).  */
516             if (ccid -> ux_device_class_ccid_n_busy > 0)
517                 ccid -> ux_device_class_ccid_n_busy --;
518 
519             /* Unlock.  */
520             ccid -> ux_device_class_ccid_flags &= ~UX_DEVICE_CLASS_CCID_FLAG_LOCK;
521 
522             /* Fall through.  */
523         case UX_DEVICE_CLASS_CCID_RSP_DONE:
524 
525             /* Start command.  */
526             if (ccid -> ux_device_class_ccid_cmd_state == UX_DEVICE_CLASS_CCID_CMD_IDLE)
527                 ccid -> ux_device_class_ccid_cmd_state = UX_DEVICE_CLASS_CCID_CMD_START;
528 
529             /* Next: idle.  */
530             ccid -> ux_device_class_ccid_rsp_state = UX_DEVICE_CLASS_CCID_RSP_IDLE;
531             return(UX_STATE_IDLE);
532 
533         default:
534             break;
535         }
536 
537         /* Break the loop.  */
538         immediate_state = UX_FALSE;
539     }
540 
541     /* Unhandled state.  */
542     return(UX_STATE_EXIT);
543 }
544 
545 #endif