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 HID 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_hid.h"
28 #include "ux_device_stack.h"
29 
30 
31 #if defined(UX_DEVICE_CLASS_HID_INTERRUPT_OUT_SUPPORT) && !defined(UX_DEVICE_STANDALONE)
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _ux_device_class_hid_receiver_thread                PORTABLE C      */
37 /*                                                           6.3.0        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Chaoqiong Xiao, Microsoft Corporation                               */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This function is the thread of the hid interrupt OUT endpoint       */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    hid_class                                 Address of hid class      */
49 /*                                                container               */
50 /*                                                                        */
51 /*  OUTPUT                                                                */
52 /*                                                                        */
53 /*    None                                                                */
54 /*                                                                        */
55 /*  CALLS                                                                 */
56 /*                                                                        */
57 /*    _ux_utility_event_flags_get           Get event flags               */
58 /*    _ux_device_stack_transfer_request     Request transfer              */
59 /*    _ux_utility_memory_copy               Copy memory                   */
60 /*    _ux_utility_thread_suspend            Suspend thread                */
61 /*                                                                        */
62 /*  CALLED BY                                                             */
63 /*                                                                        */
64 /*    ThreadX                                                             */
65 /*                                                                        */
66 /*  RELEASE HISTORY                                                       */
67 /*                                                                        */
68 /*    DATE              NAME                      DESCRIPTION             */
69 /*                                                                        */
70 /*  01-31-2022     Chaoqiong Xiao           Initial Version 6.1.10        */
71 /*  04-25-2022     Chaoqiong Xiao           Modified comment(s),          */
72 /*                                            added receiver callback,    */
73 /*                                            resulting in version 6.1.11 */
74 /*  10-31-2023     Chaoqiong Xiao           Modified comment(s),          */
75 /*                                            added zero copy support,    */
76 /*                                            resulting in version 6.3.0  */
77 /*                                                                        */
78 /**************************************************************************/
_ux_device_class_hid_receiver_thread(ULONG hid_instance)79 VOID  _ux_device_class_hid_receiver_thread(ULONG hid_instance)
80 {
81 
82 UX_SLAVE_CLASS_HID                  *hid;
83 UX_SLAVE_DEVICE                     *device;
84 UX_DEVICE_CLASS_HID_RECEIVER        *receiver;
85 UX_DEVICE_CLASS_HID_RECEIVED_EVENT  *pos;
86 UCHAR                               *next_pos;
87 UX_SLAVE_TRANSFER                   *transfer;
88 UINT                                status;
89 UCHAR                               *buffer;
90 ULONG                               temp;
91 
92 
93     /* Cast properly the hid instance.  */
94     UX_THREAD_EXTENSION_PTR_GET(hid, UX_SLAVE_CLASS_HID, hid_instance)
95 
96     /* Get the pointer to the device.  */
97     device =  &_ux_system_slave -> ux_system_slave_device;
98 
99     /* Get receiver instance.  */
100     receiver = hid -> ux_device_class_hid_receiver;
101 
102     /* This thread runs forever but can be suspended or resumed.  */
103     while(1)
104     {
105 
106         /* Check device state.  */
107         if (device -> ux_slave_device_state != UX_DEVICE_CONFIGURED)
108         {
109 
110             /* We need to suspend ourselves. We will be resumed by the device enumeration module.  */
111             _ux_utility_thread_suspend(&receiver -> ux_device_class_hid_receiver_thread);
112             continue;
113         }
114 
115         /* Protect read.  */
116         _ux_device_mutex_on(&hid -> ux_device_class_hid_read_mutex);
117 
118         /* Check if there is buffer available.  */
119         pos = receiver -> ux_device_class_hid_receiver_event_save_pos;
120         if (pos -> ux_device_class_hid_received_event_length != 0)
121         {
122 
123             /* Wait before check again.  */
124             status = _ux_utility_event_flags_get(
125                                 &hid -> ux_device_class_hid_event_flags_group,
126                                 UX_DEVICE_CLASS_HID_RECEIVER_RESTART,
127                                 UX_OR_CLEAR, &temp, 100);
128             if (status != UX_SUCCESS)
129             {
130 
131                 /* Keep checking before a good state.  */
132                 _ux_device_mutex_off(&hid -> ux_device_class_hid_read_mutex);
133                 continue;
134             }
135         }
136 
137         /* Event buffer available, issue request to get data.  */
138         transfer = &hid -> ux_device_class_hid_read_endpoint -> ux_slave_endpoint_transfer_request;
139 
140 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
141 
142         /* Directly use event buffer for transfer.  */
143         buffer = pos -> ux_device_class_hid_received_event_data;
144         transfer -> ux_slave_transfer_request_data_pointer = buffer;
145 #endif
146 
147         /* Issue the transfer request.  */
148         status = _ux_device_stack_transfer_request(transfer,
149                     receiver -> ux_device_class_hid_receiver_event_buffer_size,
150                     receiver -> ux_device_class_hid_receiver_event_buffer_size);
151 
152         /* Check status and ignore ZLPs.  */
153         if ((status != UX_SUCCESS) ||
154             (transfer -> ux_slave_transfer_request_actual_length == 0))
155         {
156             _ux_device_mutex_off(&hid -> ux_device_class_hid_read_mutex);
157             continue;
158         }
159 
160 #if (UX_DEVICE_ENDPOINT_BUFFER_OWNER == 1) && defined(UX_DEVICE_CLASS_HID_ZERO_COPY)
161 
162         /* Save received event length.  */
163         temp = transfer -> ux_slave_transfer_request_actual_length;
164 #else
165         /* Save received event data and length.  */
166         buffer = (UCHAR *)&pos -> ux_device_class_hid_received_event_data;
167         temp = transfer -> ux_slave_transfer_request_actual_length;
168         _ux_utility_memory_copy(buffer,
169                         transfer -> ux_slave_transfer_request_data_pointer,
170                         temp); /* Use case of memcpy is verified. */
171 #endif
172 
173         /* Unprotect read.  */
174         _ux_device_mutex_off(&hid -> ux_device_class_hid_read_mutex);
175 
176         /* Advance the save position.  */
177         next_pos = (UCHAR *)pos + UX_DEVICE_CLASS_HID_RECEIVED_QUEUE_ITEM_SIZE(receiver);
178 
179         if (next_pos >= (UCHAR *)receiver -> ux_device_class_hid_receiver_events_end)
180             next_pos = (UCHAR *)receiver -> ux_device_class_hid_receiver_events;
181         receiver -> ux_device_class_hid_receiver_event_save_pos = (UX_DEVICE_CLASS_HID_RECEIVED_EVENT *)next_pos;
182 
183         /* Save received data length (it's valid now).  */
184         pos -> ux_device_class_hid_received_event_length = temp;
185 
186         /* Notify application that a event is received.  */
187         if (receiver -> ux_device_class_hid_receiver_event_callback)
188             receiver -> ux_device_class_hid_receiver_event_callback(hid);
189     }
190 }
191 #endif
192