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