1 /*
2  * Copyright (c) 2015, Freescale Semiconductor, Inc.
3  * Copyright 2017 NXP
4  * All rights reserved.
5  *
6  *
7  * SPDX-License-Identifier: BSD-3-Clause
8  */
9 
10 #include "usb_device_config.h"
11 #include "usb.h"
12 #include "usb_device.h"
13 
14 #include "usb_device_class.h"
15 
16 #if ((defined(USB_DEVICE_CONFIG_DFU)) && (USB_DEVICE_CONFIG_DFU > 0U))
17 #include "usb_device_dfu.h"
18 
19 /*******************************************************************************
20  * Definitions
21  ******************************************************************************/
22 
23 /*******************************************************************************
24  * Prototypes
25  ******************************************************************************/
26 
27 static usb_status_t USB_DeviceDfuAllocateHandle(usb_device_dfu_struct_t **handle);
28 static usb_status_t USB_DeviceDfuFreeHandle(usb_device_dfu_struct_t *handle);
29 
30 /*******************************************************************************
31  * Variables
32  ******************************************************************************/
33 
34 USB_GLOBAL static usb_device_dfu_struct_t s_UsbDeviceDfuHandle[USB_DEVICE_CONFIG_DFU];
35 
36 /*******************************************************************************
37  * Code
38  ******************************************************************************/
39 
40 /*!
41  * @brief Allocate a device dfu class handle.
42  *
43  * This function allocates a device dfu class handle.
44  *
45  * @param handle          It is out parameter, is used to return pointer of the device dfu class handle to the caller.
46  *
47  * @retval kStatus_USB_Success              Get a device dfu class handle successfully.
48  * @retval kStatus_USB_Busy                 Cannot allocate a device dfu class handle.
49  */
USB_DeviceDfuAllocateHandle(usb_device_dfu_struct_t ** handle)50 static usb_status_t USB_DeviceDfuAllocateHandle(usb_device_dfu_struct_t **handle)
51 {
52     uint32_t count;
53     for (count = 0U; count < USB_DEVICE_CONFIG_DFU; count++)
54     {
55         if (NULL == s_UsbDeviceDfuHandle[count].handle)
56         {
57             *handle = &s_UsbDeviceDfuHandle[count];
58             return kStatus_USB_Success;
59         }
60     }
61 
62     return kStatus_USB_Busy;
63 }
64 
65 /*!
66  * @brief Free a device dfu class handle.
67  *
68  * This function frees a device dfu class handle.
69  *
70  * @param handle          The device dfu class handle.
71  *
72  * @retval kStatus_USB_Success              Free device dfu class handle successfully.
73  */
USB_DeviceDfuFreeHandle(usb_device_dfu_struct_t * handle)74 static usb_status_t USB_DeviceDfuFreeHandle(usb_device_dfu_struct_t *handle)
75 {
76     handle->handle       = NULL;
77     handle->configStruct = (usb_device_class_config_struct_t *)NULL;
78     return kStatus_USB_Success;
79 }
80 
81 /*!
82  * @brief Handle the event passed to the dfu class.
83  *
84  * This function handles the event passed to the dfu class.
85  *
86  * @param handle          The dfu class handle, got from the usb_device_class_config_struct_t::classHandle.
87  * @param event           The event codes. Please refer to the enumeration usb_device_class_event_t.
88  * @param param           The param type is determined by the event code.
89  *
90  * @return A USB error code or kStatus_USB_Success.
91  * @retval kStatus_USB_Success              Free device handle successfully.
92  * @retval kStatus_USB_InvalidParameter     The device handle not be found.
93  * @retval kStatus_USB_InvalidRequest       The request is invalid, and the control pipe will be stalled by the caller.
94  */
USB_DeviceDfuEvent(void * handle,uint32_t event,void * param)95 usb_status_t USB_DeviceDfuEvent(void *handle, uint32_t event, void *param)
96 {
97     usb_device_dfu_struct_t *dfuHandle;
98     usb_status_t error                 = kStatus_USB_Error;
99     usb_device_class_event_t eventCode = (usb_device_class_event_t)event;
100 
101     if ((NULL == param) || (NULL == handle))
102     {
103         return kStatus_USB_InvalidHandle;
104     }
105 
106     /* Get the dfu class handle. */
107     dfuHandle = (usb_device_dfu_struct_t *)handle;
108 
109     switch (eventCode)
110     {
111         case kUSB_DeviceClassEventDeviceReset:
112             error = kStatus_USB_Success;
113             break;
114         case kUSB_DeviceClassEventSetConfiguration:
115             error = kStatus_USB_Success;
116             break;
117         case kUSB_DeviceClassEventClassRequest:
118         {
119             /* Handle the dfu class specific request. */
120             usb_device_control_request_struct_t *controlRequest = (usb_device_control_request_struct_t *)param;
121             int32_t dfuRequest                                  = -1;
122 
123             if ((controlRequest->setup->bmRequestType & USB_REQUEST_TYPE_RECIPIENT_MASK) !=
124                 USB_REQUEST_TYPE_RECIPIENT_INTERFACE)
125             {
126                 break;
127             }
128 
129             if ((controlRequest->setup->wIndex & 0xFFU) != 0x00U /* Interface number is always 0 */)
130             {
131                 break;
132             }
133 
134             error = kStatus_USB_InvalidRequest;
135             if ((controlRequest->setup->bmRequestType & USB_REQUEST_TYPE_DIR_MASK) == USB_REQUEST_TYPE_DIR_OUT)
136             {
137                 switch (controlRequest->setup->bRequest)
138                 {
139                     case USB_DEVICE_DFU_DETACH:
140                         if (controlRequest->setup->wLength == 0U)
141                         {
142                             dfuRequest = (int32_t)kUSB_DeviceDfuEventDetach;
143                         }
144                         break;
145                     case USB_DEVICE_DFU_DNLOAD:
146                         dfuRequest = (int32_t)kUSB_DeviceDfuEventDownLoad;
147                         break;
148                     case USB_DEVICE_DFU_CLRSTATUS:
149                         if (controlRequest->setup->wLength == 0U)
150                         {
151                             dfuRequest = (int32_t)kUSB_DeviceDfuEventClearStatus;
152                         }
153                         break;
154                     case USB_DEVICE_DFU_ABORT:
155                         if (controlRequest->setup->wLength == 0U)
156                         {
157                             dfuRequest = (int32_t)kUSB_DeviceDfuEventAbort;
158                         }
159                         break;
160                     default:
161                         /* no action, return kStatus_USB_InvalidRequest */
162                         break;
163                 }
164             }
165             else
166             {
167                 switch (controlRequest->setup->bRequest)
168                 {
169                     case USB_DEVICE_DFU_UPLOAD:
170                         dfuRequest = (int32_t)kUSB_DeviceDfuEventUpLoad;
171                         break;
172                     case USB_DEVICE_DFU_GETSTATUS:
173                         if (controlRequest->setup->wLength != 0U)
174                         {
175                             dfuRequest = (int32_t)kUSB_DeviceDfuEventGetStatus;
176                         }
177                         break;
178                     case USB_DEVICE_DFU_GETSTATE:
179                         if (controlRequest->setup->wLength != 0U)
180                         {
181                             dfuRequest = (int32_t)kUSB_DeviceDfuEventGetState;
182                         }
183                         break;
184                     default:
185                         /* no action, return kStatus_USB_InvalidRequest */
186                         break;
187                 }
188             }
189 
190             if (dfuRequest != -1)
191             {
192                 /* ClassCallback is initialized in classInit of s_UsbDeviceClassInterfaceMap,
193                 it is from the second parameter of classInit */
194                 error = dfuHandle->configStruct->classCallback((class_handle_t)dfuHandle, dfuRequest, controlRequest);
195             }
196         }
197         break;
198         default:
199             /*no action*/
200             break;
201     }
202     return error;
203 }
204 
205 /*!
206  * @brief Initialize the dfu class.
207  *
208  * This function is used to initialize the dfu class.
209  *
210  * @param controllerId   The controller id of the USB IP. Please refer to the enumeration usb_controller_index_t.
211  * @param config         The class configuration information.
212  * @param handle         It is out parameter, is used to return pointer of the dfu class handle to the caller.
213  *
214  * @return A USB error code or kStatus_USB_Success.
215  */
USB_DeviceDfuInit(uint8_t controllerId,usb_device_class_config_struct_t * config,class_handle_t * handle)216 usb_status_t USB_DeviceDfuInit(uint8_t controllerId, usb_device_class_config_struct_t *config, class_handle_t *handle)
217 {
218     usb_device_dfu_struct_t *dfuHandle;
219     usb_status_t error;
220 
221     /* Allocate a dfu class handle. */
222     error = USB_DeviceDfuAllocateHandle(&dfuHandle);
223 
224     if (kStatus_USB_Success != error)
225     {
226         return error;
227     }
228 
229     /* Get the device handle according to the controller id. */
230     error = USB_DeviceClassGetDeviceHandle(controllerId, &dfuHandle->handle);
231 
232     if (kStatus_USB_Success != error)
233     {
234         return error;
235     }
236 
237     if (NULL == dfuHandle->handle)
238     {
239         return kStatus_USB_InvalidHandle;
240     }
241     /* Save the configuration of the class. */
242     dfuHandle->configStruct = config;
243 
244     *handle = (class_handle_t)dfuHandle;
245     return error;
246 }
247 
248 /*!
249  * @brief De-initialize the device dfu class.
250  *
251  * The function de-initializes the device dfu class.
252  *
253  * @param handle The dfu class handle got from usb_device_class_config_struct_t::classHandle.
254  *
255  * @return A USB error code or kStatus_USB_Success.
256  */
USB_DeviceDfuDeinit(class_handle_t handle)257 usb_status_t USB_DeviceDfuDeinit(class_handle_t handle)
258 {
259     usb_device_dfu_struct_t *dfuHandle;
260 #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U))
261     usb_status_t error = kStatus_USB_Error;
262 #endif
263 
264     dfuHandle = (usb_device_dfu_struct_t *)handle;
265 
266     if (NULL == dfuHandle)
267     {
268         return kStatus_USB_InvalidHandle;
269     }
270 
271     /* Free the dfu class handle. */
272 #if (defined(USB_DEVICE_CONFIG_RETURN_VALUE_CHECK) && (USB_DEVICE_CONFIG_RETURN_VALUE_CHECK > 0U))
273     error = USB_DeviceDfuFreeHandle(dfuHandle);
274     return error;
275 #else
276     (void)USB_DeviceDfuFreeHandle(dfuHandle);
277     return kStatus_USB_Success;
278 #endif
279 }
280 #endif
281