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