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