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 PIMA 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_pima.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_pima_thread                        PORTABLE C      */
37 /*                                                           6.1.11       */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function is the thread of the pima class.                      */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    pima_class                               Address of pima class      */
49 /*                                                container               */
50 /*                                                                        */
51 /*  OUTPUT                                                                */
52 /*                                                                        */
53 /*    None                                                                */
54 /*                                                                        */
55 /*  CALLS                                                                 */
56 /*                                                                        */
57 /*    _ux_device_stack_transfer_request     Request transfer              */
58 /*    _ux_device_stack_endpoint_stall       Stall endpoint                */
59 /*    _ux_utility_memory_allocate           Allocate memory               */
60 /*    _ux_device_semaphore_create           Create semaphore              */
61 /*    _ux_device_thread_create              Create thread                 */
62 /*    _ux_device_thread_suspend             Suspend thread                */
63 /*    _ux_utility_short_get                 Get 16-bit value              */
64 /*    _ux_utility_long_get                  Get 32-bit value              */
65 /*    _ux_device_class_pima_device_info_send                              */
66 /*                                          Send PIMA device info         */
67 /*    _ux_device_class_pima_storage_id_send Send PIMA storage ID          */
68 /*    _ux_device_class_pima_storage_info_get                              */
69 /*                                          Get PIMA storage info get     */
70 /*    _ux_device_class_pima_objects_number_send                           */
71 /*                                          Send number of PIMA objects   */
72 /*    _ux_device_class_pima_object_handles_send                           */
73 /*                                          Send PIMA object handlers     */
74 /*    _ux_device_class_pima_object_info_get Get PIMA object info          */
75 /*    _ux_device_class_pima_object_data_get Get PIMA object data          */
76 /*    _ux_device_class_pima_object_delete   Delete PIMA object            */
77 /*    _ux_device_class_pima_object_info_send                              */
78 /*                                          Send PIMA object info         */
79 /*    _ux_device_class_pima_object_data_send                              */
80 /*                                          Send PIMA object data         */
81 /*    _ux_device_class_pima_storage_format  Format storage                */
82 /*    _ux_device_class_pima_device_reset    Reset device                  */
83 /*    _ux_device_class_pima_object_props_supported_get                    */
84 /*                                          Get support PIMA object       */
85 /*                                          properties                    */
86 /*    _ux_device_class_pima_object_prop_desc_get                          */
87 /*                                          Get PIMA object property      */
88 /*                                          descriptor                    */
89 /*    _ux_device_class_pima_object_prop_value_get                         */
90 /*                                          Get PIMA object property value*/
91 /*    _ux_device_class_pima_object_prop_value_set                         */
92 /*                                          Set PIMA object property value*/
93 /*    _ux_device_class_pima_object_references_get                         */
94 /*                                          Get PIMA object references    */
95 /*    _ux_device_class_pima_device_prop_desc_get                          */
96 /*                                          Get PIMA device property      */
97 /*                                          descriptor                    */
98 /*    _ux_device_class_pima_device_prop_value_get                         */
99 /*                                          Get PIMA device property value*/
100 /*    _ux_device_class_pima_device_prop_value_set                         */
101 /*                                          Set PIMA device property value*/
102 /*    _ux_device_class_pima_response_send   Send PIMA response            */
103 /*                                                                        */
104 /*  CALLED BY                                                             */
105 /*                                                                        */
106 /*    ThreadX                                                             */
107 /*                                                                        */
108 /*  RELEASE HISTORY                                                       */
109 /*                                                                        */
110 /*    DATE              NAME                      DESCRIPTION             */
111 /*                                                                        */
112 /*  05-19-2020     Chaoqiong Xiao           Initial Version 6.0           */
113 /*  09-30-2020     Chaoqiong Xiao           Modified comment(s),          */
114 /*                                            used UX prefix to refer to  */
115 /*                                            TX symbols instead of using */
116 /*                                            them directly,              */
117 /*                                            resulting in version 6.1    */
118 /*  01-31-2022     Chaoqiong Xiao           Modified comment(s),          */
119 /*                                            refined macros names,       */
120 /*                                            updated phase states,       */
121 /*                                            refined internal function,  */
122 /*                                            resulting in version 6.1.10 */
123 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
124 /*                                            fixed standalone compile,   */
125 /*                                            resulting in version 6.1.11 */
126 /*                                                                        */
127 /**************************************************************************/
_ux_device_class_pima_thread(ULONG pima_class)128 VOID  _ux_device_class_pima_thread(ULONG pima_class)
129 {
130 
131 UX_SLAVE_CLASS              *class_ptr;
132 UX_SLAVE_CLASS_PIMA         *pima;
133 UX_SLAVE_DEVICE             *device;
134 UX_SLAVE_TRANSFER           *transfer_request;
135 UCHAR                       *pima_command;
136 ULONG                       pima_command_code;
137 ULONG                       pima_parameter_1;
138 ULONG                       pima_parameter_2;
139 ULONG                       pima_parameter_3;
140 UINT                        status;
141 
142 
143     /* Cast properly the pima instance.  */
144     UX_THREAD_EXTENSION_PTR_GET(class_ptr, UX_SLAVE_CLASS, pima_class)
145 
146     /* Get the pima instance from this class container.  */
147     pima =  (UX_SLAVE_CLASS_PIMA *) class_ptr -> ux_slave_class_instance;
148 
149     /* Allocate some memory for the thread stack. */
150     pima -> ux_device_class_pima_interrupt_thread_stack =
151             _ux_utility_memory_allocate(UX_NO_ALIGN, UX_REGULAR_MEMORY, UX_THREAD_STACK_SIZE);
152 
153     /* Check for successful allocation.  */
154     if (pima -> ux_device_class_pima_interrupt_thread_stack == UX_NULL)
155     {
156 
157         /* Do not proceed.  */
158         return;
159     }
160 
161     /* Allocate a semaphore to this thread.  */
162     status =  _ux_device_semaphore_create(&pima -> ux_device_class_pima_interrupt_thread_semaphore,
163                                           "ux_device_class_interrupt_thread_semaphore", 0);
164 
165     /* Check completion status.  */
166     if (status != UX_SUCCESS)
167     {
168 
169         /* Do not proceed.  */
170         return;
171     }
172 
173     /* The Pima device class needs 2 threads, one is activated by default for the command\response and one needs to be
174        created here for the interrupt pipe event.  */
175     status =  _ux_device_thread_create(&pima -> ux_device_class_pima_interrupt_thread, "ux_slave_class_thread_pima_interrupt",
176                 _ux_device_class_pima_interrupt_thread,
177                 (ULONG) (ALIGN_TYPE) pima, (VOID *) pima -> ux_device_class_pima_interrupt_thread_stack,
178                 UX_THREAD_STACK_SIZE, UX_THREAD_PRIORITY_CLASS,
179                 UX_THREAD_PRIORITY_CLASS, UX_NO_TIME_SLICE, UX_AUTO_START);
180 
181     /* Check the creation of this thread.  */
182     if (status != UX_SUCCESS)
183     {
184 
185         /* Do not proceed.  */
186         return;
187     }
188 
189     UX_THREAD_EXTENSION_PTR_SET(&(pima -> ux_device_class_pima_interrupt_thread), pima)
190 
191     /* This thread runs forever but can be suspended or resumed.  */
192     while(1)
193     {
194 
195 
196         /* Get the pointer to the device.  */
197         device =  &_ux_system_slave -> ux_system_slave_device;
198 
199         /* All PIMA commands are on the endpoint OUT, from the host.  */
200         transfer_request =  &pima -> ux_device_class_pima_bulk_out_endpoint -> ux_slave_endpoint_transfer_request;
201 
202         /* As long as the device is in the CONFIGURED state.  */
203         while (device -> ux_slave_device_state == UX_DEVICE_CONFIGURED)
204         {
205 
206             /* Phase idle.  */
207             pima -> ux_device_class_pima_state = UX_DEVICE_CLASS_PIMA_PHASE_IDLE;
208 
209             /* Send the request to the device controller.  */
210             status =  _ux_device_stack_transfer_request(transfer_request, 64, 64);
211 
212             /* Check the status */
213             if (status == UX_SUCCESS)
214             {
215 
216                 /* Obtain the buffer address containing the PIMA command.  */
217                 pima_command =  transfer_request -> ux_slave_transfer_request_data_pointer;
218 
219                 /* Check to make sure we have a command block.  */
220                 if (_ux_utility_short_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_TYPE) == UX_DEVICE_CLASS_PIMA_CT_COMMAND_BLOCK)
221                 {
222 
223                     /* Save the transaction ID.  */
224                     pima -> ux_device_class_pima_transaction_id = _ux_utility_long_get(pima_command +
225                                                                     UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_TRANSACTION_ID);
226 
227                     /* Retrieve the command stored in the command block.  */
228                     pima_command_code = _ux_utility_short_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_CODE);
229 
230                     /* Retrieve the parameter 1.  */
231                     pima_parameter_1 = _ux_utility_long_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_1);
232 
233                     /* Retrieve the parameter 2.  */
234                     pima_parameter_2 = _ux_utility_long_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_2);
235 
236                     /* Retrieve the parameter 3.  */
237                     pima_parameter_3 = _ux_utility_long_get(pima_command + UX_DEVICE_CLASS_PIMA_COMMAND_HEADER_PARAMETER_3);
238 
239                     /* Phase command.  */
240                     pima -> ux_device_class_pima_state = UX_DEVICE_CLASS_PIMA_PHASE_COMMAND;
241 
242                     /* We check first if this is a GET_DEVICE_INFO as this is the only command which does not require
243                        a session to be opened.  */
244 
245                     switch (pima_command_code)
246                     {
247 
248                         case UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_INFO :
249 
250                             /* Return the device info to the host.  */
251                             status = _ux_device_class_pima_device_info_send(pima);
252                             break;
253 
254                         case UX_DEVICE_CLASS_PIMA_OC_OPEN_SESSION               :
255 
256                             /* If the first parameter is 0x00000000,
257                                  the operation should fail with a response of Invalid_Parameter.
258                                If a session is already open, and the device does not support multiple sessions,
259                                  the response Session_Already_Open should be returned,
260                                  with the SessionID of the already open session as the first response parameter.
261                                The response Session_Already_Open should also be used if the device supports multiple sessions,
262                                  but a session with that ID is already open.
263                                If the device supports multiple sessions, and the maximum number of sessions are open,
264                                  the device should respond with Device_Busy  */
265                             if (pima_parameter_1 == 0)
266                             {
267                                 _ux_device_class_pima_response_send(pima,
268                                             UX_DEVICE_CLASS_PIMA_RC_INVALID_PARAMETER, 0, 0, 0, 0);
269                                 break;
270                             }
271                             /* Check if session is already opened.  */
272                             if (pima -> ux_device_class_pima_session_id != 0)
273                             {
274                                 _ux_device_class_pima_response_send(pima,
275                                             UX_DEVICE_CLASS_PIMA_RC_SESSION_ALREADY_OPENED,
276                                             pima -> ux_device_class_pima_session_id, 0, 0, 0);
277                                 break;
278                             }
279 
280                                 /* Session can be opened.  */
281                                 pima -> ux_device_class_pima_session_id =  pima_parameter_1;
282                             pima -> ux_device_class_pima_transaction_id = 0;
283                             _ux_device_class_pima_response_send(pima,
284                                         UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0);
285                             break;
286 
287                         default :
288 
289                             /* Check if a session is opened.  */
290                             if (pima -> ux_device_class_pima_session_id == 0)
291                             {
292 
293                                 /* We cannot proceed since the session is not opened.  */
294                                 _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_SESSION_NOT_OPEN, 0, 0, 0, 0);
295                             }
296                             else
297                             {
298 
299                                 /* Analyze the command stored in the command block.  */
300                                 switch (pima_command_code)
301                                 {
302 
303                                     case UX_DEVICE_CLASS_PIMA_OC_CLOSE_SESSION              :
304 
305                                         /* We close the session. Return OK.  */
306                                         _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OK, 0, 0, 0, 0);
307 
308                                         /* Session is now closed.  */
309                                         pima -> ux_device_class_pima_session_id = 0;
310                                     break;
311 
312                                     case UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_IDS            :
313 
314                                         /* Return the array of storage IDs to the host.  In this version, we support
315                                            only one storage media.  */
316                                         status = _ux_device_class_pima_storage_id_send(pima);
317                                         break;
318 
319                                     case UX_DEVICE_CLASS_PIMA_OC_GET_STORAGE_INFO           :
320 
321                                         /* Return the storage info to the host.  */
322                                         status = _ux_device_class_pima_storage_info_get(pima, pima_parameter_1);
323                                         break;
324 
325                                     case UX_DEVICE_CLASS_PIMA_OC_GET_NUM_OBJECTS            :
326 
327                                         /* Return the number of objects found in the system.  */
328                                         status = _ux_device_class_pima_objects_number_send(pima,
329                                                                         pima_parameter_1, pima_parameter_2, pima_parameter_3);
330                                         break;
331 
332                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_HANDLES         :
333 
334                                         /* Return the object handles found in the system.  */
335                                         status = _ux_device_class_pima_object_handles_send(pima,
336                                                                         pima_parameter_1, pima_parameter_2, pima_parameter_3);
337 
338                                         break;
339 
340                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_INFO            :
341 
342                                         /* Return the object info data set.  */
343                                         status = _ux_device_class_pima_object_info_get(pima, pima_parameter_1);
344                                         break;
345 
346                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT                 :
347 
348                                         /* Return the object data.  */
349                                         status = _ux_device_class_pima_object_data_get(pima, pima_parameter_1);
350                                         break;
351 
352                                     case UX_DEVICE_CLASS_PIMA_OC_DELETE_OBJECT              :
353 
354                                         /* Delete one or more objects.  */
355                                     status = _ux_device_class_pima_object_delete(pima, pima_parameter_1, pima_parameter_2);
356                                         break;
357 
358                                     case UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT_INFO           :
359 
360                                         /* Accept an object info data set.  */
361                                         status = _ux_device_class_pima_object_info_send(pima, pima_parameter_1, pima_parameter_2);
362                                         break;
363 
364                                     case UX_DEVICE_CLASS_PIMA_OC_SEND_OBJECT                :
365                                         /* Accept the object data.  */
366                                         status = _ux_device_class_pima_object_data_send(pima);
367                                         break;
368 
369                                     case UX_DEVICE_CLASS_PIMA_OC_GET_PARTIAL_OBJECT         :
370 
371                                         /* Return the partial object data.  */
372                                         status = _ux_device_class_pima_partial_object_data_get(pima, pima_parameter_1, pima_parameter_2, pima_parameter_3);
373                                         break;
374 
375                                     case UX_DEVICE_CLASS_PIMA_OC_FORMAT_STORE               :
376 
377                                         /* Format the storage device. This calls the application to reset all object handles stored
378                                            on the media.   */
379                                         status = _ux_device_class_pima_storage_format(pima, pima_parameter_1);
380                                         break;
381 
382                                     case UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE               :
383 
384                                         /* Reset the device. This calls the application to reset the device. The session is closed
385                                            but all objects retain their properties.  */
386                                         status = _ux_device_class_pima_device_reset(pima);
387                                         break;
388 
389 
390                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROPS_SUPPORTED :
391 
392                                         /* Return an Object Property Code array of supported object properties for the object format that is indicated
393                                         in the first parameter.  */
394                                         status = _ux_device_class_pima_object_props_supported_get(pima, pima_parameter_1);
395                                         break;
396 
397                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_DESC       :
398 
399                                         /* Returns the appropriate property that describes the dataset that is indicated in the first parameter.  */
400                                         status = _ux_device_class_pima_object_prop_desc_get(pima, pima_parameter_1, pima_parameter_2);
401                                         break;
402 
403                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_PROP_VALUE      :
404 
405                                         /* Returns the Object property value.  */
406                                         status = _ux_device_class_pima_object_prop_value_get(pima, pima_parameter_1, pima_parameter_2);
407                                         break;
408 
409                                     case UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROP_VALUE      :
410 
411                                         /* Sets the current value of the object property.  */
412                                         status = _ux_device_class_pima_object_prop_value_set(pima, pima_parameter_1, pima_parameter_2);
413                                         break;
414 
415                                     case UX_DEVICE_CLASS_PIMA_OC_GET_OBJECT_REFERENCES      :
416 
417                                         /* Returns the object handle references.  */
418                                         status = _ux_device_class_pima_object_references_get(pima, pima_parameter_1);
419                                         break;
420 
421                                     case UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_REFERENCES      :
422 
423                                         /* Set the object handle references.  */
424                                         status = _ux_device_class_pima_object_references_set(pima, pima_parameter_1);
425                                         break;
426 
427                                     case UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_DESC       :
428 
429                                         /* Returns the appropriate device property.  */
430                                         status = _ux_device_class_pima_device_prop_desc_get(pima, pima_parameter_1);
431                                         break;
432 
433                                     case UX_DEVICE_CLASS_PIMA_OC_GET_DEVICE_PROP_VALUE      :
434 
435                                         /* Returns the device property value.  */
436                                         status = _ux_device_class_pima_device_prop_value_get(pima, pima_parameter_1);
437                                         break;
438 
439                                     case UX_DEVICE_CLASS_PIMA_OC_SET_DEVICE_PROP_VALUE      :
440 
441                                         /* Sets the current value of the device property.  */
442                                         status = _ux_device_class_pima_device_prop_value_set(pima, pima_parameter_1);
443                                         break;
444 
445                                     case UX_DEVICE_CLASS_PIMA_OC_INITIATE_OPEN_CAPTURE      :
446                                     case UX_DEVICE_CLASS_PIMA_OC_GET_THUMB                  :
447                                     case UX_DEVICE_CLASS_PIMA_OC_INITIATE_CAPTURE           :
448                                     case UX_DEVICE_CLASS_PIMA_OC_SELF_TEST                  :
449                                     case UX_DEVICE_CLASS_PIMA_OC_SET_OBJECT_PROTECTION      :
450                                     case UX_DEVICE_CLASS_PIMA_OC_POWER_DOWN                 :
451                                     case UX_DEVICE_CLASS_PIMA_OC_RESET_DEVICE_PROP_VALUE    :
452                                     case UX_DEVICE_CLASS_PIMA_OC_TERMINATE_OPEN_CAPTURE     :
453                                     case UX_DEVICE_CLASS_PIMA_OC_MOVE_OBJECT                :
454                                     case UX_DEVICE_CLASS_PIMA_OC_COPY_OBJECT                :
455 
456                                         /* Functions not yet supported.  */
457                                         _ux_device_class_pima_response_send(pima, UX_DEVICE_CLASS_PIMA_RC_OPERATION_NOT_SUPPORTED, 0, 0, 0, 0);
458 
459                                         /* Set error code.  */
460                                         status = UX_FUNCTION_NOT_SUPPORTED;
461 
462                                         break;
463 
464                                     default:
465 
466                                     /* The command is unknown, so we stall the endpoint.  */
467                                     _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint);
468                                 }
469 
470                                 /* Check error code. */
471                                 if (status != UX_SUCCESS)
472 
473                                     /* Error trap. */
474                                     _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
475 
476                             }
477                         }
478 
479                     /* Check error code. */
480                     if (status != UX_SUCCESS)
481 
482                         /* Error trap. */
483                         _ux_system_error_handler(UX_SYSTEM_LEVEL_THREAD, UX_SYSTEM_CONTEXT_CLASS, status);
484                 }
485                 else
486 
487                     /* We have a wrong buffer format. Stall the endpoint.  */
488                     _ux_device_stack_endpoint_stall(pima -> ux_device_class_pima_bulk_out_endpoint);
489             }
490         }
491 
492     /* We need to suspend ourselves. We will be resumed by the device enumeration module.  */
493     _ux_device_thread_suspend(&class_ptr -> ux_slave_class_thread);
494     }
495 }
496 #endif
497