summaryrefslogtreecommitdiff
path: root/usb
diff options
context:
space:
mode:
authorAdrian Bradianu <adrian.bradianu@windriver.com>2016-06-01 14:20:02 +0300
committerInaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>2016-06-30 17:51:56 +0000
commit7a16ade6d12ab7db8137ab762caf5bcfa06228c1 (patch)
tree9686fe400f9c3f923fb21a5b0e3bb2d245235309 /usb
parent7a6bab308ae6a71f2c1a981c3da1ed127ddee67f (diff)
usb: Add USB device core layer
USB Device core layer is a hardware independent interface between USB device controller driver and USB device class drivers or customer applications. It's a port of the LPCUSB device stack. Change-Id: I9371ffab7034d20953fec0525e72fbe9e094c931 Signed-off-by: Adrian Bradianu <adrian.bradianu@windriver.com> Signed-off-by: Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com> Signed-off-by: Jithu Joseph <jithu.joseph@intel.com>
Diffstat (limited to 'usb')
-rw-r--r--usb/Kconfig36
-rw-r--r--usb/Makefile3
-rw-r--r--usb/include/usb_device.h229
-rw-r--r--usb/include/usbstruct.h111
-rw-r--r--usb/usb_device.c926
5 files changed, 1305 insertions, 0 deletions
diff --git a/usb/Kconfig b/usb/Kconfig
new file mode 100644
index 000000000..6fe1ff5fd
--- /dev/null
+++ b/usb/Kconfig
@@ -0,0 +1,36 @@
+# Kconfig - USB device stack configuration options
+
+#
+# Copyright (c) 2016 Wind River Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+menuconfig USB_DEVICE_STACK
+ bool
+ prompt "USB device stack"
+ depends on USB
+ default n
+ help
+ Enable USB device stack.
+
+if USB_DEVICE_STACK
+
+config USB_DEBUG
+ bool
+ prompt "Enable USB debug options for the entire USB stack"
+ default n
+ help
+ This option enables the debug features for the USB device stack.
+
+endif # USB_DEVICE_STACK
diff --git a/usb/Makefile b/usb/Makefile
new file mode 100644
index 000000000..f4ad7c5dc
--- /dev/null
+++ b/usb/Makefile
@@ -0,0 +1,3 @@
+ccflags-y += -I${srctree}/include/drivers/usb -I${srctree}/usb/include
+
+obj-$(CONFIG_USB_DEVICE_STACK) += usb_device.o
diff --git a/usb/include/usb_device.h b/usb/include/usb_device.h
new file mode 100644
index 000000000..c71e97ed6
--- /dev/null
+++ b/usb/include/usb_device.h
@@ -0,0 +1,229 @@
+/*
+ * LPCUSB, an USB device driver for LPC microcontrollers
+ * Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief USB device core layer APIs and structures
+ *
+ * This file contains the USB device core layer APIs and structures.
+ */
+
+#ifndef USB_DEVICE_H_
+#define USB_DEVICE_H_
+
+#include "usb_dc.h"
+#include "usbstruct.h"
+
+/*************************************************************************
+ * USB configuration
+ **************************************************************************/
+
+#define MAX_PACKET_SIZE0 64 /**< maximum packet size for EP 0 */
+
+/*************************************************************************
+ * USB application interface
+ **************************************************************************/
+
+/** setup packet definitions */
+struct usb_setup_packet {
+ uint8_t bmRequestType; /**< characteristics of the specific request */
+ uint8_t bRequest; /**< specific request */
+ uint16_t wValue; /**< request specific parameter */
+ uint16_t wIndex; /**< request specific parameter */
+ uint16_t wLength; /**< length of data transferred in data phase */
+};
+
+/**
+ * Callback function signature for the device
+ */
+typedef void (*usb_status_callback)(enum usb_dc_status_code status_code);
+
+/**
+ * Callback function signature for the USB Endpoint status
+ */
+typedef void (*usb_ep_callback)(uint8_t ep,
+ enum usb_dc_ep_cb_status_code cb_status);
+
+/**
+ * Function which handles Class specific requests corresponding to an
+ * interface number specified in the device descriptor table
+ */
+typedef int (*usb_request_handler) (struct usb_setup_packet *detup,
+ int *transfer_len, uint8_t **payload_data);
+
+/*
+ * USB Endpoint Configuration
+ */
+struct usb_ep_cfg_data {
+ /**
+ * Callback function for notification of data received and
+ * available to application or transmit done, NULL if callback
+ * not required by application code
+ */
+ usb_ep_callback ep_cb;
+ /**
+ * The number associated with the EP in the device configuration
+ * structure
+ * IN EP = 0x80 | \<endpoint number\>
+ * OUT EP = 0x00 | \<endpoint number\>
+ */
+ uint8_t ep_addr;
+};
+
+/**
+ * USB Interface Configuration
+ */
+struct usb_interface_cfg_data {
+ /** Handler for USB Class specific Control (EP 0) communications */
+ usb_request_handler class_handler;
+ /**
+ * The custom request handler gets a first chance at handling
+ * the request before it is handed over to the 'chapter 9' request
+ * handler
+ */
+ usb_request_handler custom_handler;
+ /**
+ * This data area, allocated by the application, is used to store
+ * Class specific command data and must be large enough to store the
+ * largest payload associated with the largest supported Class'
+ * command set. This data area may be used for USB IN or OUT
+ * communications
+ */
+ uint8_t *payload_data;
+};
+
+/*
+ * @brief USB device configuration
+ *
+ * The Application instantiates this with given parameters added
+ * using the "usb_set_config" function. Once this function is called
+ * changes to this structure will result in undefined behaviour. This structure
+ * may only be updated after calls to usb_deconfig
+ */
+struct usb_cfg_data {
+ /**
+ * USB device description, see
+ * http://www.beyondlogic.org/usbnutshell/usb5.shtml#DeviceDescriptors
+ */
+ const uint8_t *usb_device_description;
+ /** Callback to be notified on USB connection status change */
+ usb_status_callback cb_usb_status;
+ /** USB interface (Class) handler and storage space */
+ struct usb_interface_cfg_data interface;
+ /** Number of individual endpoints in the device configuration */
+ uint8_t num_endpoints;
+ /**
+ * Pointer to an array of endpoint structs of length equal to the
+ * number of EP associated with the device description,
+ * not including control endpoints
+ */
+ struct usb_ep_cfg_data *endpoint;
+};
+
+/*
+ * @brief configure USB controller
+ *
+ * Function to configure USB controller.
+ * Configuration parameters must be valid or an error is returned
+ *
+ * @param[in] config Pointer to configuration structure
+ *
+ * @return 0 on success, negative errno code on fail
+ */
+int usb_set_config(struct usb_cfg_data *config);
+
+/*
+ * @brief return the USB device to it's initial state
+ *
+ * @return 0 on success, negative errno code on fail
+ */
+int usb_deconfig(void);
+
+/*
+ * @brief enable USB for host/device connection
+ *
+ * Function to enable USB for host/device connection.
+ * Upon success, the USB module is no longer clock gated in hardware,
+ * it is now capable of transmitting and receiving on the USB bus and
+ * of generating interrupts.
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usb_enable(struct usb_cfg_data *config);
+
+/*
+ * @brief disable the USB device.
+ *
+ * Function to disable the USB device.
+ * Upon success, the specified USB interface is clock gated in hardware,
+ * it is no longer capable of generating interrupts.
+ *
+ * @return 0 on success, negative errno code on fail
+ */
+int usb_disable(void);
+
+/*
+ * @brief write data to the specified endpoint
+ *
+ * Function to write data to the specified endpoint. The supplied
+ * usb_ep_callback will be called when transmission is done.
+ *
+ * @param[in] ep Endpoint address corresponding to the one listed in the
+ * device configuration table
+ * @param[in] data Pointer to data to write
+ * @param[in] data_len Length of data requested to write. This may be zero for
+ * a zero length status packet.
+ * @param[out] bytes_ret Bytes written to the EP FIFO. This value may be NULL if
+ * the application expects all bytes to be written
+ *
+ * @return 0 on success, negative errno code on fail
+ */
+int usb_write(uint8_t ep, const uint8_t *data, uint32_t data_len,
+ uint32_t *bytes_ret);
+
+/*
+ * @brief read data from the specified endpoint
+ *
+ * This function is called by the Endpoint handler function, after an
+ * OUT interrupt has been received for that EP. The application must
+ * only call this function through the supplied usb_ep_callback function.
+ *
+ * @param[in] ep Endpoint address corresponding to the one listed in
+ * the device configuration table
+ * @param[in] data Pointer to data buffer to write to
+ * @param[in] max_data_len Max length of data to read
+ * @param[out] ret_bytes Number of bytes read. If data is NULL and
+ * max_data_len is 0 the number of bytes available
+ * for read is returned.
+ *
+ * @return 0 on success, negative errno code on fail
+ */
+int usb_read(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *ret_bytes);
+
+#endif /* USB_DEVICE_H_ */
diff --git a/usb/include/usbstruct.h b/usb/include/usbstruct.h
new file mode 100644
index 000000000..229065a2a
--- /dev/null
+++ b/usb/include/usbstruct.h
@@ -0,0 +1,111 @@
+/*
+ * LPCUSB, an USB device driver for LPC microcontrollers
+ * Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief standard USB packet stuctures and defines
+ *
+ * This file contains stuctures and defines of the standard USB packets
+ */
+
+#ifndef _USBSTRUCT_H_
+#define _USBSTRUCT_H_
+
+#define REQTYPE_GET_DIR(x) (((x)>>7)&0x01)
+#define REQTYPE_GET_TYPE(x) (((x)>>5)&0x03)
+#define REQTYPE_GET_RECIP(x) ((x)&0x1F)
+
+#define REQTYPE_DIR_TO_DEVICE 0
+#define REQTYPE_DIR_TO_HOST 1
+
+#define REQTYPE_TYPE_STANDARD 0
+#define REQTYPE_TYPE_CLASS 1
+#define REQTYPE_TYPE_VENDOR 2
+#define REQTYPE_TYPE_RESERVED 3
+
+#define REQTYPE_RECIP_DEVICE 0
+#define REQTYPE_RECIP_INTERFACE 1
+#define REQTYPE_RECIP_ENDPOINT 2
+#define REQTYPE_RECIP_OTHER 3
+
+/* standard requests */
+#define REQ_GET_STATUS 0x00
+#define REQ_CLEAR_FEATURE 0x01
+#define REQ_SET_FEATURE 0x03
+#define REQ_SET_ADDRESS 0x05
+#define REQ_GET_DESCRIPTOR 0x06
+#define REQ_SET_DESCRIPTOR 0x07
+#define REQ_GET_CONFIGURATION 0x08
+#define REQ_SET_CONFIGURATION 0x09
+#define REQ_GET_INTERFACE 0x0A
+#define REQ_SET_INTERFACE 0x0B
+#define REQ_SYNCH_FRAME 0x0C
+
+/* class requests HID */
+#define HID_GET_REPORT 0x01
+#define HID_GET_IDLE 0x02
+#define HID_GET_PROTOCOL 0x03
+#define HID_SET_REPORT 0x09
+#define HID_SET_IDLE 0x0A
+#define HID_SET_PROTOCOL 0x0B
+
+/* feature selectors */
+#define FEA_ENDPOINT_HALT 0x00
+#define FEA_REMOTE_WAKEUP 0x01
+#define FEA_TEST_MODE 0x02
+
+/*
+ * USB descriptors
+ */
+
+/** USB descriptor header */
+struct usb_desc_header {
+ uint8_t bLength; /**< descriptor length */
+ uint8_t bDescriptorType; /**< descriptor type */
+};
+
+#define DESC_DEVICE 1
+#define DESC_CONFIGURATION 2
+#define DESC_STRING 3
+#define DESC_INTERFACE 4
+#define DESC_ENDPOINT 5
+#define DESC_DEVICE_QUALIFIER 6
+#define DESC_OTHER_SPEED 7
+#define DESC_INTERFACE_POWER 8
+
+#define CS_INTERFACE 0x24
+#define CS_ENDPOINT 0x25
+
+#define DESC_HID_HID 0x21
+#define DESC_HID_REPORT 0x22
+#define DESC_HID_PHYSICAL 0x23
+
+#define GET_DESC_TYPE(x) (((x)>>8)&0xFF)
+#define GET_DESC_INDEX(x) ((x)&0xFF)
+
+#endif /* _USBSTRUCT_H_ */
diff --git a/usb/usb_device.c b/usb/usb_device.c
new file mode 100644
index 000000000..966e135d0
--- /dev/null
+++ b/usb/usb_device.c
@@ -0,0 +1,926 @@
+/*
+ * LPCUSB, an USB device driver for LPC microcontrollers
+ * Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file
+ * @brief USB device core layer
+ *
+ * This module handles control transfer handler, standard request handler and
+ * USB Interface for customer application.
+ *
+ * Control transfers handler is normally installed on the
+ * endpoint 0 callback.
+ *
+ * Control transfers can be of the following type:
+ * 0 Standard;
+ * 1 Class;
+ * 2 Vendor;
+ * 3 Reserved.
+ *
+ * A callback can be installed for each of these control transfers using
+ * usb_register_request_handler.
+ * When an OUT request arrives, data is collected in the data store provided
+ * with the usb_register_request_handler call. When the transfer is done, the
+ * callback is called.
+ * When an IN request arrives, the callback is called immediately to either
+ * put the control transfer data in the data store, or to get a pointer to
+ * control transfer data. The data is then packetised and sent to the host.
+ *
+ * Standard request handler handles the 'chapter 9' processing, specifically
+ * the standard device requests in table 9-3 from the universal serial bus
+ * specification revision 2.0
+ */
+
+#include <errno.h>
+#include <stddef.h>
+#include <misc/util.h>
+#include <misc/__assert.h>
+#include <board.h>
+#if defined(USB_VUSB_EN_GPIO)
+#include <gpio.h>
+#endif
+#include "usb_device.h"
+
+#ifndef CONFIG_USB_DEBUG
+#define DBG(...) { ; }
+#else
+#if defined(CONFIG_STDOUT_CONSOLE)
+#include <stdio.h>
+#define DBG printf
+#else
+#include <misc/printk.h>
+#define DBG printk
+#endif /* CONFIG_STDOUT_CONSOLE */
+#endif /* CONFIG_USB_DEBUG */
+
+#define MAX_DESC_HANDLERS 4 /** Device, interface, endpoint, other */
+
+/* general descriptor field offsets */
+#define DESC_bLength 0 /** Length offset */
+#define DESC_bDescriptorType 1 /** Descriptor type offset */
+
+/* config descriptor field offsets */
+#define CONF_DESC_wTotalLength 2 /** Total length offset */
+#define CONF_DESC_bConfigurationValue 5 /** Configuration value offset */
+#define CONF_DESC_bmAttributes 7 /** configuration characteristics */
+
+/* interface descriptor field offsets */
+#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
+
+/* endpoint descriptor field offsets */
+#define ENDP_DESC_bEndpointAddress 2 /** Endpoint address offset */
+#define ENDP_DESC_bmAttributes 3 /** Bulk or interrupt? */
+#define ENDP_DESC_wMaxPacketSize 4 /** Maximum packet size offset */
+
+#define MAX_NUM_REQ_HANDLERS (4)
+#define MAX_STD_REQ_MSG_SIZE 8
+
+/* Default USB control EP, always 0 and 0x80 */
+#define USB_CONTROL_OUT_EP0 0
+#define USB_CONTROL_IN_EP0 0x80
+
+static struct usb_dev_priv {
+ /** Setup packet */
+ struct usb_setup_packet setup;
+ /** Pointer to data buffer */
+ uint8_t *data_buf;
+ /** Eemaining bytes in buffer */
+ int32_t data_buf_residue;
+ /** Total length of control transfer */
+ int32_t data_buf_len;
+ /** Installed custom request handler */
+ usb_request_handler custom_req_handler;
+ /** USB stack status clalback */
+ usb_status_callback status_callback;
+ /** Pointer to registered descriptors */
+ const uint8_t *descriptors;
+ /** Array of installed request handler callbacks */
+ usb_request_handler req_handlers[MAX_NUM_REQ_HANDLERS];
+ /** Array of installed request data pointers */
+ uint8_t *data_store[MAX_NUM_REQ_HANDLERS];
+ /* Buffer used for storing standard usb request data */
+ uint8_t std_req_data[MAX_STD_REQ_MSG_SIZE];
+ /** Variable to check whether the usb has been enabled */
+ bool enabled;
+ /** Currently selected configuration */
+ uint8_t configuration;
+} usb_dev;
+
+/*
+ * @brief print the contents of a setup packet
+ *
+ * @param [in] setup The setup packet
+ *
+ */
+static void usb_print_setup(struct usb_setup_packet *setup)
+{
+ /* avoid compiler warning if DBG is not defined */
+ setup = setup;
+
+ DBG("SETUP\n");
+ DBG("%x %x %x %x %x\n",
+ setup->bmRequestType,
+ setup->bRequest,
+ setup->wValue,
+ setup->wIndex,
+ setup->wLength);
+}
+
+/*
+ * @brief handle a request by calling one of the installed request handlers
+ *
+ * Local function to handle a request by calling one of the installed request
+ * handlers. In case of data going from host to device, the data is at *ppbData.
+ * In case of data going from device to host, the handler can either choose to
+ * write its data at *ppbData or update the data pointer.
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in,out] data Data buffer
+ *
+ * @return true if the request was handles successfully
+ */
+static bool usb_handle_request(struct usb_setup_packet *setup,
+ int32_t *len, uint8_t **data)
+{
+ uint32_t type = REQTYPE_GET_TYPE(setup->bmRequestType);
+ usb_request_handler handler = usb_dev.req_handlers[type];
+
+ DBG("** %d **\n", type);
+
+ if (type >= MAX_NUM_REQ_HANDLERS) {
+ DBG("Error Incorrect iType %d\n", type);
+ return false;
+ }
+
+ if (handler == NULL) {
+ DBG("No handler for reqtype %d\n", type);
+ return false;
+ }
+
+ if ((*handler)(setup, len, data) < 0) {
+ DBG("Handler Error %d\n", type);
+ usb_print_setup(setup);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * @brief send next chunk of data (possibly 0 bytes) to host
+ *
+ * @return N/A
+ */
+static void usb_data_to_host(void)
+{
+ uint32_t chunk = min(MAX_PACKET_SIZE0, usb_dev.data_buf_residue);
+
+ /*Always EP0 for control*/
+ usb_dc_ep_write(0x80, usb_dev.data_buf, chunk, &chunk);
+ usb_dev.data_buf += chunk;
+ usb_dev.data_buf_residue -= chunk;
+}
+
+/*
+ * @brief handle IN/OUT transfers on EP0
+ *
+ * @param [in] ep Endpoint address
+ * @param [in] ep_status Endpoint status
+ *
+ * @return N/A
+ */
+static void usb_handle_control_transfer(uint8_t ep,
+ enum usb_dc_ep_cb_status_code ep_status)
+{
+ uint32_t chunk = 0;
+ uint32_t type = 0;
+ struct usb_setup_packet *setup = &usb_dev.setup;
+
+ DBG("usb_handle_control_transfer ep %x, status %x\n", ep, ep_status);
+ if (ep == USB_CONTROL_OUT_EP0 && ep_status == USB_DC_EP_SETUP) {
+ /*
+ * OUT transfer, Setup packet,
+ * reset request message state machine
+ */
+ if (usb_dc_ep_read(ep,
+ (uint8_t *)setup, sizeof(*setup), NULL) < 0) {
+ DBG("Read Setup Packet failed\n");
+ usb_dc_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ /* Defaults for data pointer and residue */
+ type = REQTYPE_GET_TYPE(setup->bmRequestType);
+ usb_dev.data_buf = usb_dev.data_store[type];
+ usb_dev.data_buf_residue = setup->wLength;
+ usb_dev.data_buf_len = setup->wLength;
+
+ if (!(setup->wLength == 0) &&
+ !(REQTYPE_GET_DIR(setup->bmRequestType) ==
+ REQTYPE_DIR_TO_HOST)) {
+ return;
+ }
+
+ /* Ask installed handler to process request */
+ if (!usb_handle_request(setup,
+ &usb_dev.data_buf_len, &usb_dev.data_buf)) {
+ DBG("usb_handle_request failed\n");
+ usb_dc_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ /* Send smallest of requested and offered length */
+ usb_dev.data_buf_residue = min(usb_dev.data_buf_len,
+ setup->wLength);
+ /* Send first part (possibly a zero-length status message) */
+ usb_data_to_host();
+ } else if (ep == USB_CONTROL_OUT_EP0) {
+ /* OUT transfer, data or status packets */
+ if (usb_dev.data_buf_residue <= 0) {
+ /* absorb zero-length status message */
+ if (usb_dc_ep_read(USB_CONTROL_OUT_EP0,
+ usb_dev.data_buf, 0, &chunk) < 0) {
+ DBG("Read DATA Packet failed\n");
+ usb_dc_ep_set_stall(USB_CONTROL_IN_EP0);
+ }
+ DBG(chunk > 0 ? "?" : "");
+ return;
+ }
+
+ if (usb_dc_ep_read(USB_CONTROL_OUT_EP0,
+ usb_dev.data_buf,
+ usb_dev.data_buf_residue, &chunk) < 0) {
+ DBG("Read DATA Packet failed\n");
+ usb_dc_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ usb_dev.data_buf += chunk;
+ usb_dev.data_buf_residue -= chunk;
+ if (usb_dev.data_buf_residue == 0) {
+ /* Received all, send data to handler */
+ type = REQTYPE_GET_TYPE(setup->bmRequestType);
+ usb_dev.data_buf = usb_dev.data_store[type];
+ if (!usb_handle_request(setup,
+ &usb_dev.data_buf_len, &usb_dev.data_buf)) {
+ DBG("usb_handle_request1 failed\n");
+ usb_dc_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ /*Send status to host*/
+ DBG(">> usb_data_to_host(2)\n");
+ usb_data_to_host();
+ }
+ } else if (ep == USB_CONTROL_IN_EP0) {
+ /* Send more data if available */
+ if (usb_dev.data_buf_residue != 0) {
+ usb_data_to_host();
+ }
+ } else {
+ __ASSERT_NO_MSG(false);
+ }
+}
+
+
+/*
+ * @brief register a callback for handling requests
+ *
+ * @param [in] type Type of request, e.g. REQTYPE_TYPE_STANDARD
+ * @param [in] handler Callback function pointer
+ * @param [in] data_store Data storage area for this type of request
+ *
+ * @return N/A
+ */
+static void usb_register_request_handler(int32_t type,
+ usb_request_handler handler, uint8_t *data_store)
+{
+ usb_dev.req_handlers[type] = handler;
+ usb_dev.data_store[type] = data_store;
+}
+
+/*
+ * @brief register a pointer to a descriptor block
+ *
+ * This function registers a pointer to a descriptor block containing all
+ * descriptors for the device.
+ *
+ * @param [in] usb_descriptors The descriptor byte array
+ */
+static void usb_register_descriptors(const uint8_t *usb_descriptors)
+{
+ usb_dev.descriptors = usb_descriptors;
+}
+
+/*
+ * @brief get specified USB descriptor
+ *
+ * This function parses the list of installed USB descriptors and attempts
+ * to find the specified USB descriptor.
+ *
+ * @param [in] type_index Type and index of the descriptor
+ * @param [in] lang_id Language ID of the descriptor (currently unused)
+ * @param [out] len Descriptor length
+ * @param [out] data Descriptor data
+ *
+ * @return true if the descriptor was found, false otherwise
+ */
+static bool usb_get_descriptor(uint16_t type_index, uint16_t lang_id,
+ int32_t *len, uint8_t **data)
+{
+ uint8_t type = 0;
+ uint8_t index = 0;
+ uint8_t *p = NULL;
+ int32_t cur_index = 0;
+ bool found = false;
+
+ __ASSERT_NO_MSG(usb_descriptors != NULL);
+
+ /*Avoid compiler warning until this is used for something*/
+ lang_id = lang_id;
+
+ type = GET_DESC_TYPE(type_index);
+ index = GET_DESC_INDEX(type_index);
+
+ p = (uint8_t *)usb_dev.descriptors;
+ cur_index = 0;
+
+ while (p[DESC_bLength] != 0) {
+ if (p[DESC_bDescriptorType] == type) {
+ if (cur_index == index) {
+ found = true;
+ break;
+ }
+ cur_index++;
+ }
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+ if (found) {
+ /* set data pointer */
+ *data = p;
+ /* get length from structure */
+ if (type == DESC_CONFIGURATION) {
+ /* configuration descriptor is an
+ * exception, length is at offset
+ * 2 and 3
+ */
+ *len = (p[CONF_DESC_wTotalLength]) |
+ (p[CONF_DESC_wTotalLength + 1] << 8);
+ } else {
+ /* normally length is at offset 0 */
+ *len = p[DESC_bLength];
+ }
+ } else {
+ /* nothing found */
+ DBG("Desc %x not found!\n", type_index);
+ }
+ return found;
+}
+
+/*
+ * @brief set USB configuration
+ *
+ * This function configures the device according to the specified configuration
+ * index and alternate setting by parsing the installed USB descriptor list.
+ * A configuration index of 0 unconfigures the device.
+ *
+ * @param [in] config_index Configuration index
+ * @param [in] alt_setting Alternate setting number
+ *
+ * @return true if successfully configured false if error or unconfigured
+ */
+static bool usb_set_configuration(uint8_t config_index, uint8_t alt_setting)
+{
+ uint8_t *p = NULL;
+ uint8_t cur_config = 0;
+ uint8_t cur_alt_setting = 0;
+
+ __ASSERT_NO_MSG(usb_descriptors != NULL);
+
+ if (config_index == 0) {
+ /* unconfigure device */
+ DBG("Device not configured - invalid configuration offset\n");
+ return true;
+ }
+
+ /* configure endpoints for this configuration/altsetting */
+ p = (uint8_t *)usb_dev.descriptors;
+ cur_config = 0xFF;
+ cur_alt_setting = 0xFF;
+
+ while (p[DESC_bLength] != 0) {
+ switch (p[DESC_bDescriptorType]) {
+ case DESC_CONFIGURATION:
+ /* remember current configuration index */
+ cur_config = p[CONF_DESC_bConfigurationValue];
+ break;
+
+ case DESC_INTERFACE:
+ /* remember current alternate setting */
+ cur_alt_setting =
+ p[INTF_DESC_bAlternateSetting];
+ break;
+
+ case DESC_ENDPOINT:
+ if ((cur_config == config_index) &&
+ (cur_alt_setting == alt_setting)) {
+ struct usb_dc_ep_cfg_data ep_cfg;
+ /* endpoint found for desired config
+ * and alternate setting
+ */
+ ep_cfg.ep_type =
+ p[ENDP_DESC_bmAttributes];
+ ep_cfg.ep_mps =
+ (p[ENDP_DESC_wMaxPacketSize]) |
+ (p[ENDP_DESC_wMaxPacketSize + 1]
+ << 8);
+ ep_cfg.ep_addr =
+ p[ENDP_DESC_bEndpointAddress];
+ usb_dc_ep_configure(&ep_cfg);
+ usb_dc_ep_enable(ep_cfg.ep_addr);
+ }
+ break;
+
+ default:
+ break;
+ }
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+ if (usb_dev.status_callback)
+ usb_dev.status_callback(USB_DC_CONFIGURED);
+
+ return true;
+}
+
+/*
+ * @brief handle a standard device request
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in,out] data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static bool usb_handle_std_device_req(struct usb_setup_packet *setup,
+ int32_t *len, uint8_t **data_buf)
+{
+ bool ret = true;
+ uint8_t *data = *data_buf;
+
+ switch (setup->bRequest) {
+ case REQ_GET_STATUS:
+ DBG("REQ_GET_STATUS\n");
+ /* bit 0: self-powered */
+ /* bit 1: remote wakeup = not supported */
+ data[0] = 0;
+ data[1] = 0;
+ *len = 2;
+ break;
+
+ case REQ_SET_ADDRESS:
+ DBG("REQ_SET_ADDRESS\n");
+ usb_dc_set_address(setup->wValue);
+ break;
+
+ case REQ_GET_DESCRIPTOR:
+ DBG("REQ_GET_DESCRIPTOR\n");
+ ret = usb_get_descriptor(setup->wValue,
+ setup->wIndex, len, data_buf);
+ break;
+
+ case REQ_GET_CONFIGURATION:
+ DBG("REQ_GET_CONFIGURATION\n");
+ /* indicate if we are configured */
+ data[0] = usb_dev.configuration;
+ *len = 1;
+ break;
+
+ case REQ_SET_CONFIGURATION:
+ DBG("REQ_SET_CONFIGURATION\n");
+ if (!usb_set_configuration(setup->wValue & 0xFF, 0)) {
+ DBG("USBSetConfiguration failed!\n");
+ ret = false;
+ } else {
+ /* configuration successful,
+ * update current configuration
+ */
+ usb_dev.configuration = setup->wValue & 0xFF;
+ }
+ break;
+
+ case REQ_CLEAR_FEATURE:
+ DBG("REQ_CLEAR_FEATURE\n");
+ break;
+ case REQ_SET_FEATURE:
+ DBG("REQ_SET_FEATURE\n");
+
+ if (setup->wValue == FEA_REMOTE_WAKEUP) {
+ /* put DEVICE_REMOTE_WAKEUP code here */
+ }
+
+ if (setup->wValue == FEA_TEST_MODE) {
+ /* put TEST_MODE code here */
+ }
+ ret = false;
+ break;
+
+ case REQ_SET_DESCRIPTOR:
+ DBG("Device req %x not implemented\n", setup->bRequest);
+ ret = false;
+ break;
+
+ default:
+ DBG("Illegal device req %x\n", setup->bRequest);
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * @brief handle a standard interface request
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in] data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static bool usb_handle_std_interface_req(struct usb_setup_packet *setup,
+ int32_t *len, uint8_t **data_buf)
+{
+ uint8_t *data = *data_buf;
+
+ switch (setup->bRequest) {
+ case REQ_GET_STATUS:
+ /* no bits specified */
+ data[0] = 0;
+ data[1] = 0;
+ *len = 2;
+ break;
+
+ case REQ_CLEAR_FEATURE:
+ case REQ_SET_FEATURE:
+ /* not defined for interface */
+ return false;
+
+ case REQ_GET_INTERFACE:
+ /* there is only one interface, return n-1 (= 0) */
+ data[0] = 0;
+ *len = 1;
+ break;
+
+ case REQ_SET_INTERFACE:
+ DBG("REQ_SET_INTERFACE\n");
+ *len = 0;
+ break;
+
+ default:
+ DBG("Illegal interface req %d\n", setup->bRequest);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * @brief handle a standard endpoint request
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in] data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static bool usb_handle_std_endpoint_req(struct usb_setup_packet *setup,
+ int32_t *len, uint8_t **data_buf)
+{
+ uint8_t *data = *data_buf;
+
+ switch (setup->bRequest) {
+ case REQ_GET_STATUS:
+ /* bit 0 = endpointed halted or not */
+ usb_dc_ep_is_stalled(setup->wIndex, &data[0]);
+ data[1] = 0;
+ *len = 2;
+ break;
+
+ case REQ_CLEAR_FEATURE:
+ if (setup->wValue == FEA_ENDPOINT_HALT) {
+ /* clear HALT by unstalling */
+ usb_dc_ep_clear_stall(setup->wIndex);
+ break;
+ }
+ /* only ENDPOINT_HALT defined for endpoints */
+ return false;
+
+ case REQ_SET_FEATURE:
+ if (setup->wValue == FEA_ENDPOINT_HALT) {
+ /* set HALT by stalling */
+ usb_dc_ep_set_stall(setup->wIndex);
+ break;
+ }
+ /* only ENDPOINT_HALT defined for endpoints */
+ return false;
+
+ case REQ_SYNCH_FRAME:
+ DBG("EP req %d not implemented\n", setup->bRequest);
+ return false;
+
+ default:
+ DBG("Illegal EP req %d\n", setup->bRequest);
+ return false;
+ }
+
+ return true;
+}
+
+
+/*
+ * @brief default handler for standard ('chapter 9') requests
+ *
+ * If a custom request handler was installed, this handler is called first.
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in] data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static int usb_handle_standard_request(struct usb_setup_packet *setup,
+ int32_t *len, uint8_t **data_buf)
+{
+ int rc = 0;
+ /* try the custom request handler first */
+ if ((usb_dev.custom_req_handler != NULL) &&
+ (!usb_dev.custom_req_handler(setup, len, data_buf)))
+ return 0;
+
+ switch (REQTYPE_GET_RECIP(setup->bmRequestType)) {
+ case REQTYPE_RECIP_DEVICE:
+ if (usb_handle_std_device_req(setup, len, data_buf) == false)
+ rc = -EINVAL;
+ break;
+ case REQTYPE_RECIP_INTERFACE:
+ if (usb_handle_std_interface_req(setup, len, data_buf) == false)
+ rc = -EINVAL;
+ break;
+ case REQTYPE_RECIP_ENDPOINT:
+ if (usb_handle_std_endpoint_req(setup, len, data_buf) == false)
+ rc = -EINVAL;
+ break;
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+
+/*
+ * @brief Registers a callback for custom device requests
+ *
+ * In usb_register_custom_req_handler, the custom request handler gets a first
+ * chance at handling the request before it is handed over to the 'chapter 9'
+ * request handler.
+ *
+ * This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR
+ * request is sent to an interface, which is not covered by the 'chapter 9'
+ * specification.
+ *
+ * @param [in] handler Callback function pointer
+ */
+static void usb_register_custom_req_handler(usb_request_handler handler)
+{
+ usb_dev.custom_req_handler = handler;
+}
+
+/*
+ * @brief register a callback for device status
+ *
+ * This function registers a callback for device status. The registered callback
+ * is used to report changes in the status of the device controller.
+ *
+ * @param [in] cb Callback function pointer
+ */
+static void usb_register_status_callback(usb_status_callback cb)
+{
+ usb_dev.status_callback = cb;
+}
+
+/**
+ * @brief turn on/off USB VBUS voltage
+ *
+ * @param on Set to false to turn off and to true to turn on VBUS
+ *
+ * @return 0 on success, negative errno code on fail
+ */
+static int usb_vbus_set(bool on)
+{
+#if defined(USB_VUSB_EN_GPIO)
+ int ret = 0;
+ struct device *gpio_dev = device_get_binding(USB_GPIO_DRV_NAME);
+
+ if (!gpio_dev) {
+ DBG("USB requires GPIO. Cannot find %s!\n", USB_GPIO_DRV_NAME);
+ return -ENODEV;
+ }
+
+ /* Enable USB IO */
+ ret = gpio_pin_configure(gpio_dev, USB_VUSB_EN_GPIO, GPIO_DIR_OUT);
+ if (ret)
+ return ret;
+
+ ret = gpio_pin_write(gpio_dev, USB_VUSB_EN_GPIO, on == true ? 1 : 0);
+ if (ret)
+ return ret;
+#endif
+
+ return 0;
+}
+
+int usb_set_config(struct usb_cfg_data *config)
+{
+ if (!config)
+ return -EINVAL;
+
+ /* register descriptors */
+ usb_register_descriptors(config->usb_device_description);
+
+ /* register standard request handler */
+ usb_register_request_handler(REQTYPE_TYPE_STANDARD,
+ &(usb_handle_standard_request), usb_dev.std_req_data);
+
+ /* register class request handlers for each interface*/
+ if (config->interface.class_handler != NULL) {
+ usb_register_request_handler(REQTYPE_TYPE_CLASS,
+ config->interface.class_handler,
+ config->interface.payload_data);
+ }
+ /* register class request handlers for each interface*/
+ if (config->interface.custom_handler != NULL) {
+ usb_register_custom_req_handler(
+ config->interface.custom_handler);
+ }
+
+ /* register status callback */
+ if (config->cb_usb_status != NULL) {
+ usb_register_status_callback(config->cb_usb_status);
+ }
+
+ return 0;
+}
+
+int usb_deconfig(void)
+{
+ /* unregister descriptors */
+ usb_register_descriptors(NULL);
+
+ /* unegister standard request handler */
+ usb_register_request_handler(REQTYPE_TYPE_STANDARD, NULL, NULL);
+
+ /* unregister class request handlers for each interface*/
+ usb_register_request_handler(REQTYPE_TYPE_CLASS, NULL, NULL);
+
+ /* unregister class request handlers for each interface*/
+ usb_register_custom_req_handler(NULL);
+
+ /* unregister status callback */
+ usb_register_status_callback(NULL);
+
+ /* Reset USB controller */
+ usb_dc_reset();
+
+ return 0;
+}
+
+int usb_enable(struct usb_cfg_data *config)
+{
+ int ret;
+ uint32_t i;
+ struct usb_dc_ep_cfg_data ep0_cfg;
+
+ if (true == usb_dev.enabled) {
+ return 0;
+ }
+
+ /* Enable VBUS if needed */
+ ret = usb_vbus_set(true);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_dc_set_status_callback(config->cb_usb_status);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_dc_attach();
+ if (ret < 0)
+ return ret;
+
+ /* Configure control EP */
+ ep0_cfg.ep_mps = MAX_PACKET_SIZE0;
+ ep0_cfg.ep_type = USB_DC_EP_CONTROL;
+
+ ep0_cfg.ep_addr = USB_CONTROL_OUT_EP0;
+ ret = usb_dc_ep_configure(&ep0_cfg);
+ if (ret < 0)
+ return ret;
+
+ ep0_cfg.ep_addr = USB_CONTROL_IN_EP0;
+ ret = usb_dc_ep_configure(&ep0_cfg);
+ if (ret < 0)
+ return ret;
+
+ /*register endpoint 0 handlers*/
+ ret = usb_dc_ep_set_callback(USB_CONTROL_OUT_EP0,
+ usb_handle_control_transfer);
+ if (ret < 0)
+ return ret;
+ ret = usb_dc_ep_set_callback(USB_CONTROL_IN_EP0,
+ usb_handle_control_transfer);
+ if (ret < 0)
+ return ret;
+
+ /*register endpoint handlers*/
+ for (i = 0; i < config->num_endpoints; i++) {
+ ret = usb_dc_ep_set_callback(config->endpoint[i].ep_addr,
+ config->endpoint[i].ep_cb);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* enable control EP */
+ ret = usb_dc_ep_enable(USB_CONTROL_OUT_EP0);
+ if (ret < 0)
+ return ret;
+
+ ret = usb_dc_ep_enable(USB_CONTROL_IN_EP0);
+ if (ret < 0)
+ return ret;
+
+ usb_dev.enabled = true;
+
+ return 0;
+}
+
+int usb_disable(void)
+{
+ int ret;
+
+ if (true != usb_dev.enabled) {
+ /*Already disabled*/
+ return 0;
+ }
+
+ ret = usb_dc_detach();
+ if (ret < 0)
+ return ret;
+
+ /* Disable VBUS if needed */
+ usb_vbus_set(false);
+
+ usb_dev.enabled = false;
+
+ return 0;
+}
+
+int usb_write(uint8_t ep, const uint8_t *data, uint32_t data_len,
+ uint32_t *bytes_ret)
+{
+ return usb_dc_ep_write(ep, data, data_len, bytes_ret);
+}
+
+int usb_read(uint8_t ep, uint8_t *data, uint32_t max_data_len,
+ uint32_t *ret_bytes)
+{
+ return usb_dc_ep_read(ep, data, max_data_len, ret_bytes);
+}