aboutsummaryrefslogtreecommitdiffstats
path: root/usb/class/dfu/device/dfudf.c
diff options
context:
space:
mode:
Diffstat (limited to 'usb/class/dfu/device/dfudf.c')
-rw-r--r--usb/class/dfu/device/dfudf.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/usb/class/dfu/device/dfudf.c b/usb/class/dfu/device/dfudf.c
new file mode 100644
index 0000000..7747a23
--- /dev/null
+++ b/usb/class/dfu/device/dfudf.c
@@ -0,0 +1,186 @@
+/**
+ * \file
+ *
+ * \brief USB Device Stack DFU Function Implementation.
+ *
+ * Copyright (c) 2018 sysmocom -s.f.m.c. GmbH, Author: Kevin Redon <kredon@sysmocom.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "dfudf.h"
+#include "usb_protocol_dfu.h"
+
+/** USB Device DFU Fucntion Specific Data */
+struct dfudf_func_data {
+ /** DFU Interface information */
+ uint8_t func_iface;
+ /** DFU Enable Flag */
+ bool enabled;
+};
+
+static struct usbdf_driver _dfudf;
+static struct dfudf_func_data _dfudf_funcd;
+
+/**
+ * \brief Enable DFU Function
+ * \param[in] drv Pointer to USB device function driver
+ * \param[in] desc Pointer to USB interface descriptor
+ * \return Operation status.
+ */
+static int32_t dfudf_enable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
+{
+ struct dfudf_func_data *func_data = (struct dfudf_func_data *)(drv->func_data);
+
+ usb_iface_desc_t ifc_desc;
+ uint8_t * ifc;
+ uint8_t i;
+
+ ifc = desc->sod;
+ for (i = 0; i < 2; i++) {
+ if (NULL == ifc) {
+ return ERR_NOT_FOUND;
+ }
+
+ ifc_desc.bInterfaceNumber = ifc[2];
+ ifc_desc.bInterfaceClass = ifc[5];
+
+ if (USB_DFU_CLASS == ifc_desc.bInterfaceClass) {
+ if (func_data->func_iface == ifc_desc.bInterfaceNumber) { // Initialized
+ return ERR_ALREADY_INITIALIZED;
+ } else if (func_data->func_iface != 0xFF) { // Occupied
+ return ERR_NO_RESOURCE;
+ } else {
+ func_data->func_iface = ifc_desc.bInterfaceNumber;
+ }
+ } else { // Not supported by this function driver
+ return ERR_NOT_FOUND;
+ }
+
+ // there are no endpoint to install since DFU uses only the control endpoint
+
+ ifc = usb_find_desc(usb_desc_next(desc->sod), desc->eod, USB_DT_INTERFACE);
+ }
+ // Installed
+ _dfudf_funcd.enabled = true;
+ return ERR_NONE;
+}
+
+/**
+ * \brief Disable DFU Function
+ * \param[in] drv Pointer to USB device function driver
+ * \param[in] desc Pointer to USB device descriptor
+ * \return Operation status.
+ */
+static int32_t dfudf_disable(struct usbdf_driver *drv, struct usbd_descriptors *desc)
+{
+ struct dfudf_func_data *func_data = (struct dfudf_func_data *)(drv->func_data);
+
+ usb_iface_desc_t ifc_desc;
+
+ if (desc) {
+ ifc_desc.bInterfaceClass = desc->sod[5];
+ // Check interface
+ if (ifc_desc.bInterfaceClass != USB_DFU_CLASS) {
+ return ERR_NOT_FOUND;
+ }
+ }
+
+ func_data->func_iface = 0xFF;
+
+ _dfudf_funcd.enabled = false;
+ return ERR_NONE;
+}
+
+/**
+ * \brief DFU Control Function
+ * \param[in] drv Pointer to USB device function driver
+ * \param[in] ctrl USB device general function control type
+ * \param[in] param Parameter pointer
+ * \return Operation status.
+ */
+static int32_t dfudf_ctrl(struct usbdf_driver *drv, enum usbdf_control ctrl, void *param)
+{
+ switch (ctrl) {
+ case USBDF_ENABLE:
+ return dfudf_enable(drv, (struct usbd_descriptors *)param);
+
+ case USBDF_DISABLE:
+ return dfudf_disable(drv, (struct usbd_descriptors *)param);
+
+ case USBDF_GET_IFACE:
+ return ERR_UNSUPPORTED_OP;
+
+ default:
+ return ERR_INVALID_ARG;
+ }
+}
+
+/**
+ * \brief Process the CDC class request
+ * \param[in] ep Endpoint address.
+ * \param[in] req Pointer to the request.
+ * \return Operation status.
+ */
+static int32_t dfudf_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
+{
+ if (0x01 != ((req->bmRequestType >> 5) & 0x03)) { // class request
+ return ERR_NOT_FOUND;
+ }
+ if ((req->wIndex == _dfudf_funcd.func_iface)) {
+ // we don't verify the bmRequestType
+ switch (req->bRequest) {
+ default:
+ return ERR_UNSUPPORTED_OP;
+ }
+ } else {
+ return ERR_NOT_FOUND;
+ }
+}
+
+/** USB Device DFU Handler Struct */
+static struct usbdc_handler dfudf_req_h = {NULL, (FUNC_PTR)dfudf_req};
+
+/**
+ * \brief Initialize the USB DFU Function Driver
+ */
+int32_t dfudf_init(void)
+{
+ if (usbdc_get_state() > USBD_S_POWER) {
+ return ERR_DENIED;
+ }
+
+ _dfudf.ctrl = dfudf_ctrl;
+ _dfudf.func_data = &_dfudf_funcd;
+
+ usbdc_register_function(&_dfudf);
+ usbdc_register_handler(USBDC_HDL_REQ, &dfudf_req_h);
+ return ERR_NONE;
+}
+
+/**
+ * \brief Deinitialize the USB DFU Function Driver
+ */
+void dfudf_deinit(void)
+{
+}
+
+/**
+ * \brief Check whether DFU Function is enabled
+ */
+bool dfudf_is_enabled(void)
+{
+ return _dfudf_funcd.enabled;
+}