diff --git a/doc/connectivity/usb/device_next/api/udc.rst b/doc/connectivity/usb/device_next/api/udc.rst index 1e62c6a66ac..752cca59479 100644 --- a/doc/connectivity/usb/device_next/api/udc.rst +++ b/doc/connectivity/usb/device_next/api/udc.rst @@ -16,3 +16,5 @@ API reference ************* .. doxygengroup:: udc_api + +.. doxygengroup:: udc_buf diff --git a/doc/connectivity/usb/device_next/usb_device.rst b/doc/connectivity/usb/device_next/usb_device.rst index a9009d54e2b..97864c29070 100644 --- a/doc/connectivity/usb/device_next/usb_device.rst +++ b/doc/connectivity/usb/device_next/usb_device.rst @@ -1,57 +1,215 @@ .. _usb_device_stack_next: -New experimental USB device support -################################### +New USB device support +###################### Overview ******** -The new USB device support is experimental. It consists of :ref:`udc_api` -and :ref:`usbd_api`. The new device stack brings support for multiple device -controllers, support for multiple configurations, and dynamic registration of -class instances to a configuration at runtime. The stack also provides a specific -class API that should be used to implement the functions (classes). -It will replace :ref:`usb_device_stack`. +USB device support consists of the USB device controller (UDC) drivers +, :ref:`udc_api`, and USB device stack, :ref:`usbd_api`. +The :ref:`udc_api` provides a generic and vendor independent interface to USB +device controllers, and although, there a is clear separation between these +layers, the purpose of :ref:`udc_api` is to serve new Zephyr's USB device stack +exclusively. -If you would like to play around with the new device support, or the new USB -support in general, please try :zephyr:code-sample:`usb-shell` sample. The sample is mainly to help -test the capabilities of the stack and correct implementation of the USB controller -drivers. +The new device stack supports multiple device controllers, meaning that if a +SoC has multiple controllers, they can be used simultaneously. Full and +high-speed device controllers are supported. It also provides support for +registering multiple function or class instances to a configuration at runtime, +or changing the configuration later. It has built-in support for several USB +classes and provides an API to implement custom USB functions. -Supported USB classes -********************* +The new USB device support is considered experimental and will replace +:ref:`usb_device_stack`. -Bluetooth HCI USB transport layer -================================= +Built-in functions +================== + +The USB device stack has built-in USB functions. Some can be used directly in +the user application through a special API, such as HID or Audio class devices, +while others use a general Zephyr RTOS driver API, such as MSC and CDC class +implementations. The *Identification string* identifies a class or function +instance (n) and is used as an argument to the :c:func:`usbd_register_class`. -See :ref:`bluetooth-hci-usb-sample` sample for reference. -To build the sample for the new device support, set the configuration -``-DCONF_FILE=usbd_next_prj.conf`` either directly or via ``west``. ++-----------------------------------+-------------------------+-------------------------+ +| Class or function | User API (if any) | Identification string | ++===================================+=========================+=========================+ +| USB Audio 2 class | :ref:`uac2_device` | uac2_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB CDC ACM class | :ref:`uart_api` | cdc_acm_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB CDC ECM class | Ethernet device | cdc_ecm_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB Mass Storage Class (MSC) | :ref:`disk_access_api` | msc_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| USB Human Interface Devices (HID) | :ref:`usbd_hid_device` | hid_(n) | ++-----------------------------------+-------------------------+-------------------------+ +| Bluetooth HCI USB transport layer | :ref:`bt_hci_raw` | bt_hci_(n) | ++-----------------------------------+-------------------------+-------------------------+ -CDC ACM +Samples ======= -CDC ACM implementation has support for multiple instances. -Description from :ref:`usb_device_cdc_acm` also applies to the new implementation. -See :zephyr:code-sample:`usb-cdc-acm` sample for reference. -To build the sample for the new device support, set the configuration -``-DCONF_FILE=usbd_next_prj.conf`` either directly or via ``west``. +* :zephyr:code-sample:`usb-hid-keyboard` -Mass Storage Class -================== +* :zephyr:code-sample:`uac2-explicit-feedback` + +Samples ported to new USB device support +---------------------------------------- + +To build a sample that supports both the old and new USB device stack, set the +configuration ``-DCONF_FILE=usbd_next_prj.conf`` either directly or via +``west``. + +* :ref:`bluetooth-hci-usb-sample` + +* :zephyr:code-sample:`usb-cdc-acm` + +* :zephyr:code-sample:`usb-cdc-acm-console` + +* :zephyr:code-sample:`usb-mass` + +* :zephyr:code-sample:`usb-hid-mouse` + +* :zephyr:code-sample:`zperf` To build the sample for the new device support, + set the configuration overlay file + ``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file + ``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``. + +How to configure and enable USB device support +********************************************** + +For the USB device support samples in the Zephyr project repository, we have a +common file for instantiation, configuration and initialization, +:zephyr_file:`samples/subsys/usb/common/sample_usbd_init.c`. The following code +snippets from this file are used as examples. USB Samples Kconfig options used +in the USB samples and prefixed with ``SAMPLE_USBD_`` have default values +specific to the Zephyr project and the scope is limited to the project samples. +In the examples below, you will need to replace these Kconfig options and other +defaults with values appropriate for your application or hardware. + +The USB device stack requires a context structure to manage its properties and +runtime data. The preferred way to define a device context is to use the +:c:macro:`USBD_DEVICE_DEFINE` macro. This creates a static +:c:struct:`usbd_context` variable with a given name. Any number of contexts may +be instantiated. A USB controller device can be assigned to multiple contexts, +but only one context can be initialized and used at a time. Context properties +must not be directly accessed or manipulated by the application. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc device instantiation start + :end-before: doc device instantiation end + +Your USB device may have manufacturer, product, and serial number string +descriptors. To instantiate these string descriptors, the application should +use the appropriate :c:macro:`USBD_DESC_MANUFACTURER_DEFINE`, +:c:macro:`USBD_DESC_PRODUCT_DEFINE`, and +:c:macro:`USBD_DESC_SERIAL_NUMBER_DEFINE` macros. String descriptors also +require a single instantiation of the language descriptor using the +:c:macro:`USBD_DESC_LANG_DEFINE` macro. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc string instantiation start + :end-before: doc string instantiation end + +String descriptors must be added to the device context at runtime before +initializing the USB device with :c:func:`usbd_add_descriptor`. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc add string descriptor start + :end-before: doc add string descriptor end + +USB device requires at least one configuration instance per supported speed. +The application should use :c:macro:`USBD_CONFIGURATION_DEFINE` to instantiate +a configuration. Later, USB device functions are assigned to a configuration. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc configuration instantiation start + :end-before: doc configuration instantiation end + +Each configuration instance for a specific speed must be added to the device +context at runtime before the USB device is initialized using +:c:func:`usbd_add_configuration`. Note :c:enumerator:`USBD_SPEED_FS` and +:c:enumerator:`USBD_SPEED_HS`. The first full-speed or high-speed +configuration will get ``bConfigurationValue`` one, and then further upward. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc configuration register start + :end-before: doc configuration register end + + +Although we have already done a lot, this USB device has no function. A device +can have multiple configurations with different set of functions at different +speeds. A function or class can be registered on a USB device before +it is initialized using :c:func:`usbd_register_class`. The desired +configuration is specified using :c:enumerator:`USBD_SPEED_FS` or +:c:enumerator:`USBD_SPEED_HS` and the configuration number. For simple cases, +:c:func:`usbd_register_all_classes` can be used to register all available +instances. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc functions register start + :end-before: doc functions register end + +The last step in the preparation is to initialize the device with +:c:func:`usbd_init`. After this, the configuration of the device cannot be +changed. A device can be deinitialized with :c:func:`usbd_shutdown` and all +instances can be reused, but the previous steps must be repeated. So it is +possible to shutdown a device, register another type of configuration or +function, and initialize it again. At the USB controller level, +:c:func:`usbd_init` does only what is necessary to detect VBUS changes. There +are controller types where the next step is only possible if a VBUS signal is +present. + +A function or class implementation may require its own specific configuration +steps, which should be performed prior to initializing the USB device. + +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :start-after: doc device init start + :end-before: doc device init end + +The final step to enable the USB device is :c:func:`usbd_enable`, after that, +if the USB device is connected to a USB host controller, the host can start +enumerating the device. The application can disable the USB device using +:c:func:`usbd_disable`. + +.. literalinclude:: ../../../../samples/subsys/usb/hid-keyboard/src/main.c + :language: c + :start-after: doc device enable start + :end-before: doc device enable end + +USB Message notifications +========================= + +The application can register a callback using :c:func:`usbd_msg_register_cb` to +receive message notification from the USB device support subsystem. The +messages are mostly about the common device state changes, and a few specific +types from the USB CDC ACM implementation. -See :zephyr:code-sample:`usb-mass` sample for reference. -To build the sample for the new device support, set the configuration -``-DCONF_FILE=usbd_next_prj.conf`` either directly or via ``west``. +.. literalinclude:: ../../../../samples/subsys/usb/common/sample_usbd_init.c + :language: c + :dedent: + :start-after: doc device init-and-msg start + :end-before: doc device init-and-msg end -Networking -========== +The helper function :c:func:`usbd_msg_type_string()` can be used to convert +:c:enumerator:`usbd_msg_type` to a human readable form for logging. -At the moment only CDC ECM class is implemented and has support for multiple instances. -It provides a virtual Ethernet connection between the remote (USB host) and -Zephyr network support. +If the controller supports VBUS state change detection, the battery-powered +application may want to enable the USB device only when it is connected to a +host. A generic application should use :c:func:`usbd_can_detect_vbus` to check +for this capability. -See :zephyr:code-sample:`zperf` for reference. -To build the sample for the new device support, set the configuration overlay file -``-DDEXTRA_CONF_FILE=overlay-usbd_next_ecm.conf`` and devicetree overlay file -``-DDTC_OVERLAY_FILE="usbd_next_ecm.overlay`` either directly or via ``west``. +.. literalinclude:: ../../../../samples/subsys/usb/hid-keyboard/src/main.c + :language: c + :dedent: + :start-after: doc device msg-cb start + :end-before: doc device msg-cb end diff --git a/drivers/kscan/kscan_input.c b/drivers/kscan/kscan_input.c index 76df309b96b..17481fc57d6 100644 --- a/drivers/kscan/kscan_input.c +++ b/drivers/kscan/kscan_input.c @@ -23,8 +23,9 @@ struct kscan_input_data { bool pressed; }; -static void kscan_input_cb(const struct device *dev, struct input_event *evt) +static void kscan_input_cb(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct kscan_input_data *data = dev->data; switch (evt->code) { @@ -100,13 +101,9 @@ static const struct kscan_driver_api kscan_input_driver_api = { }; #define KSCAN_INPUT_INIT(index) \ - static void kscan_input_cb_##index(struct input_event *evt) \ - { \ - kscan_input_cb(DEVICE_DT_GET(DT_INST(index, DT_DRV_COMPAT)), \ - evt); \ - } \ INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(index)), \ - kscan_input_cb_##index); \ + kscan_input_cb, \ + (void *)DEVICE_DT_INST_GET(index)); \ static const struct kscan_input_config kscan_input_config_##index = { \ .input_dev = DEVICE_DT_GET(DT_INST_PARENT(index)), \ }; \ diff --git a/drivers/usb/common/usb_dwc2_hw.h b/drivers/usb/common/usb_dwc2_hw.h index b137a526ede..9cd172bdd11 100644 --- a/drivers/usb/common/usb_dwc2_hw.h +++ b/drivers/usb/common/usb_dwc2_hw.h @@ -147,11 +147,45 @@ BUILD_ASSERT(sizeof(struct usb_dwc2_reg) == 0x0D00); /* AHB configuration register */ #define USB_DWC2_GAHBCFG 0x0008UL +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_POS 27UL +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_MASK (0x3UL << USB_DWC2_GAHBCFG_LOA_EOP_WORD_POS) +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_ONE 1 +#define USB_DWC2_GAHBCFG_LOA_EOP_WORD_TWO 2 +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_POS 25UL +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_MASK (0x3UL << USB_DWC2_GAHBCFG_LOA_EOP_BYTE_POS) +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_ONE 1 +#define USB_DWC2_GAHBCFG_LOA_EOP_BYTE_TWO 2 +#define USB_DWC2_GAHBCFG_INVDESCENDIANESS_POS 24UL +#define USB_DWC2_GAHBCFG_INVDESCENDIANESS BIT(USB_DWC2_GAHBCFG_INVDESCENDIANESS_POS) +#define USB_DWC2_GAHBCFG_AHBSINGLE_POS 23UL +#define USB_DWC2_GAHBCFG_AHBSINGLE BIT(USB_DWC2_GAHBCFG_AHBSINGLE_POS) +#define USB_DWC2_GAHBCFG_NOTIALLDMAWRIT_POS 22UL +#define USB_DWC2_GAHBCFG_NOTIALLDMAWRIT BIT(USB_DWC2_GAHBCFG_NOTIALLDMAWRIT_POS) +#define USB_DWC2_GAHBCFG_REMMEMSUPP_POS 21UL +#define USB_DWC2_GAHBCFG_REMMEMSUPP BIT(USB_DWC2_GAHBCFG_REMMEMSUPP_POS) +#define USB_DWC2_GAHBCFG_PTXFEMPLVL_POS 8UL +#define USB_DWC2_GAHBCFG_PTXFEMPLVL BIT(USB_DWC2_GAHBCFG_PTXFEMPLVL_POS) +#define USB_DWC2_GAHBCFG_NPTXFEMPLVL_POS 7UL +#define USB_DWC2_GAHBCFG_NPTXFEMPLVL BIT(USB_DWC2_GAHBCFG_NPTXFEMPLVL_POS) #define USB_DWC2_GAHBCFG_DMAEN_POS 5UL #define USB_DWC2_GAHBCFG_DMAEN BIT(USB_DWC2_GAHBCFG_DMAEN_POS) +#define USB_DWC2_GAHBCFG_HBSTLEN_POS 1UL +#define USB_DWC2_GAHBCFG_HBSTLEN_MASK (0xFUL << USB_DWC2_GAHBCFG_HBSTLEN_POS) +#define USB_DWC2_GAHBCFG_HBSTLEN_SINGLE 0 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR 1 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR4 3 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR8 5 +#define USB_DWC2_GAHBCFG_HBSTLEN_INCR16 7 #define USB_DWC2_GAHBCFG_GLBINTRMASK_POS 0UL #define USB_DWC2_GAHBCFG_GLBINTRMASK BIT(USB_DWC2_GAHBCFG_GLBINTRMASK_POS) +USB_DWC2_SET_FIELD_DEFINE(gahbcfg_loa_eop_word, GAHBCFG_LOA_EOP_WORD) +USB_DWC2_SET_FIELD_DEFINE(gahbcfg_loa_eop_byte, GAHBCFG_LOA_EOP_BYTE) +USB_DWC2_SET_FIELD_DEFINE(gahbcfg_hbstlen, GAHBCFG_HBSTLEN) +USB_DWC2_GET_FIELD_DEFINE(gahbcfg_loa_eop_word, GAHBCFG_LOA_EOP_WORD) +USB_DWC2_GET_FIELD_DEFINE(gahbcfg_loa_eop_byte, GAHBCFG_LOA_EOP_BYTE) +USB_DWC2_GET_FIELD_DEFINE(gahbcfg_hbstlen, GAHBCFG_HBSTLEN) + /* USB configuration register */ #define USB_DWC2_GUSBCFG 0x000CUL #define USB_DWC2_GUSBCFG_FORCEDEVMODE_POS 30UL @@ -208,6 +242,7 @@ USB_DWC2_SET_FIELD_DEFINE(grstctl_txfnum, GRSTCTL_TXFNUM) #define USB_DWC2_GINTSTS_FETSUSP BIT(USB_DWC2_GINTSTS_FETSUSP_POS) #define USB_DWC2_GINTSTS_INCOMPIP_POS 21UL #define USB_DWC2_GINTSTS_INCOMPIP BIT(USB_DWC2_GINTSTS_INCOMPIP_POS) +#define USB_DWC2_GINTSTS_INCOMPISOOUT USB_DWC2_GINTSTS_INCOMPIP #define USB_DWC2_GINTSTS_INCOMPISOIN_POS 20UL #define USB_DWC2_GINTSTS_INCOMPISOIN BIT(USB_DWC2_GINTSTS_INCOMPISOIN_POS) #define USB_DWC2_GINTSTS_OEPINT_POS 19UL @@ -411,6 +446,18 @@ USB_DWC2_GET_FIELD_DEFINE(ghwcfg4_numctleps, GHWCFG4_NUMCTLEPS) USB_DWC2_GET_FIELD_DEFINE(ghwcfg4_phydatawidth, GHWCFG4_PHYDATAWIDTH) USB_DWC2_GET_FIELD_DEFINE(ghwcfg4_numdevperioeps, GHWCFG4_NUMDEVPERIOEPS) +/* GDFIFOCFG register */ +#define USB_DWC2_GDFIFOCFG 0x005CUL +#define USB_DWC2_GDFIFOCFG_EPINFOBASEADDR_POS 16UL +#define USB_DWC2_GDFIFOCFG_EPINFOBASEADDR_MASK (0xFFFFUL << USB_DWC2_GDFIFOCFG_EPINFOBASEADDR_POS) +#define USB_DWC2_GDFIFOCFG_GDFIFOCFG_POS 0UL +#define USB_DWC2_GDFIFOCFG_GDFIFOCFG_MASK (0xFFFFUL << USB_DWC2_GDFIFOCFG_GDFIFOCFG_POS) + +USB_DWC2_GET_FIELD_DEFINE(gdfifocfg_epinfobaseaddr, GDFIFOCFG_EPINFOBASEADDR) +USB_DWC2_GET_FIELD_DEFINE(gdfifocfg_gdfifocfg, GDFIFOCFG_GDFIFOCFG) +USB_DWC2_SET_FIELD_DEFINE(gdfifocfg_epinfobaseaddr, GDFIFOCFG_EPINFOBASEADDR) +USB_DWC2_SET_FIELD_DEFINE(gdfifocfg_gdfifocfg, GDFIFOCFG_GDFIFOCFG) + /* Device IN endpoint transmit FIFO size register */ #define USB_DWC2_DIEPTXF1 0x0104UL #define USB_DWC2_DIEPTXF_INEPNTXFDEP_POS 16UL @@ -425,8 +472,38 @@ USB_DWC2_SET_FIELD_DEFINE(dieptxf_inepntxfstaddr, DIEPTXF_INEPNTXFSTADDR) /* Device configuration registers */ #define USB_DWC2_DCFG 0x0800UL +#define USB_DWC2_DCFG_RESVALID_POS 26UL +#define USB_DWC2_DCFG_RESVALID_MASK (0x3FUL << USB_DWC2_DCFG_RESVALID_POS) +#define USB_DWC2_DCFG_PERSCHINTVL_POS 24UL +#define USB_DWC2_DCFG_PERSCHINTVL_MASK (0x3UL << USB_DWC2_DCFG_PERSCHINTVL_POS) +#define USB_DWC2_DCFG_PERSCHINTVL_MF25 0 +#define USB_DWC2_DCFG_PERSCHINTVL_MF50 1 +#define USB_DWC2_DCFG_PERSCHINTVL_MF75 2 +#define USB_DWC2_DCFG_PERSCHINTVL_RESERVED 3 +#define USB_DWC2_DCFG_DESCDMA_POS 23UL +#define USB_DWC2_DCFG_DESCDMA BIT(USB_DWC2_DCFG_DESCDMA_POS) +#define USB_DWC2_DCFG_EPMISCNT_POS 18UL +#define USB_DWC2_DCFG_EPMISCNT_MASK (0x1FUL << USB_DWC2_DCFG_EPMISCNT_POS) +#define USB_DWC2_DCFG_IPGISOCSUPT_POS 17UL +#define USB_DWC2_DCFG_IPGISOCSUPT BIT(USB_DWC2_DCFG_IPGISOCSUPT_POS) +#define USB_DWC2_DCFG_ERRATICINTMSK_POS 15UL +#define USB_DWC2_DCFG_ERRATICINTMSK BIT(USB_DWC2_DCFG_ERRATICINTMSK_POS) +#define USB_DWC2_DCFG_XCVRDLY_POS 14UL +#define USB_DWC2_DCFG_XCVRDLY BIT(USB_DWC2_DCFG_XCVRDLY_POS) +#define USB_DWC2_DCFG_ENDEVOUTNAK_POS 13UL +#define USB_DWC2_DCFG_ENDEVOUTNAK BIT(USB_DWC2_DCFG_ENDEVOUTNAK_POS) +#define USB_DWC2_DCFG_PERFRINT_POS 11UL +#define USB_DWC2_DCFG_PERFRINT_MASK (0x3UL << USB_DWC2_DCFG_PERFRINT_POS) +#define USB_DWC2_DCFG_PERFRINT_EOPF80 0 +#define USB_DWC2_DCFG_PERFRINT_EOPF85 1 +#define USB_DWC2_DCFG_PERFRINT_EOPF90 2 +#define USB_DWC2_DCFG_PERFRINT_EOPF95 3 #define USB_DWC2_DCFG_DEVADDR_POS 4UL #define USB_DWC2_DCFG_DEVADDR_MASK (0x7FUL << USB_DWC2_DCFG_DEVADDR_POS) +#define USB_DWC2_DCFG_ENA32KHZSUSP_POS 3UL +#define USB_DWC2_DCFG_ENA32KHZSUSP BIT(USB_DWC2_DCFG_ENA32KHZSUSP_POS) +#define USB_DWC2_DCFG_NZSTSOUTHSHK_POS 2UL +#define USB_DWC2_DCFG_NZSTSOUTHSHK BIT(USB_DWC2_DCFG_NZSTSOUTHSHK_POS) #define USB_DWC2_DCFG_DEVSPD_POS 0UL #define USB_DWC2_DCFG_DEVSPD_MASK (0x03UL << USB_DWC2_DCFG_DEVSPD_POS) #define USB_DWC2_DCFG_DEVSPD_USBHS20 0 @@ -434,7 +511,17 @@ USB_DWC2_SET_FIELD_DEFINE(dieptxf_inepntxfstaddr, DIEPTXF_INEPNTXFSTADDR) #define USB_DWC2_DCFG_DEVSPD_USBLS116 2 #define USB_DWC2_DCFG_DEVSPD_USBFS1148 3 +USB_DWC2_SET_FIELD_DEFINE(dcfg_resvalid, DCFG_RESVALID) +USB_DWC2_SET_FIELD_DEFINE(dcfg_perschintvl, DCFG_PERSCHINTVL) +USB_DWC2_SET_FIELD_DEFINE(dcfg_epmiscnt, DCFG_EPMISCNT) +USB_DWC2_SET_FIELD_DEFINE(dcfg_perfrint, DCFG_PERFRINT) USB_DWC2_SET_FIELD_DEFINE(dcfg_devaddr, DCFG_DEVADDR) +USB_DWC2_SET_FIELD_DEFINE(dcfg_devspd, DCFG_DEVSPD) +USB_DWC2_GET_FIELD_DEFINE(dcfg_resvalid, DCFG_RESVALID) +USB_DWC2_GET_FIELD_DEFINE(dcfg_perschintvl, DCFG_PERSCHINTVL) +USB_DWC2_GET_FIELD_DEFINE(dcfg_epmiscnt, DCFG_EPMISCNT) +USB_DWC2_GET_FIELD_DEFINE(dcfg_perfrint, DCFG_PERFRINT) +USB_DWC2_GET_FIELD_DEFINE(dcfg_devaddr, DCFG_DEVADDR) USB_DWC2_GET_FIELD_DEFINE(dcfg_devspd, DCFG_DEVSPD) /* Device control register */ @@ -479,13 +566,23 @@ USB_DWC2_SET_FIELD_DEFINE(dctl_tstctl, DCTL_TSTCTL) /* Device status register */ #define USB_DWC2_DSTS 0x0808UL +#define USB_DWC2_DSTS_DEVLNSTS_POS 22UL +#define USB_DWC2_DSTS_DEVLNSTS_MASK (0x3UL << USB_DWC2_DSTS_DEVLNSTS_POS) +#define USB_DWC2_DSTS_SOFFN_POS 8UL +#define USB_DWC2_DSTS_SOFFN_MASK (0x3FFFUL << USB_DWC2_DSTS_SOFFN_POS) +#define USB_DWC2_DSTS_ERRTICERR_POS 3UL +#define USB_DWC2_DSTS_ERRTICERR BIT(USB_DWC2_DSTS_ERRTICERR_POS) #define USB_DWC2_DSTS_ENUMSPD_POS 1UL #define USB_DWC2_DSTS_ENUMSPD_MASK (0x3UL << USB_DWC2_DSTS_ENUMSPD_POS) #define USB_DWC2_DSTS_ENUMSPD_HS3060 0 #define USB_DWC2_DSTS_ENUMSPD_FS3060 1 #define USB_DWC2_DSTS_ENUMSPD_LS6 2 #define USB_DWC2_DSTS_ENUMSPD_FS48 3 +#define USB_DWC2_DSTS_SUSPSTS_POS 0UL +#define USB_DWC2_DSTS_SUSPSTS BIT(USB_DWC2_DSTS_SUSPSTS_POS) +USB_DWC2_GET_FIELD_DEFINE(dsts_devlnsts, DSTS_DEVLNSTS) +USB_DWC2_GET_FIELD_DEFINE(dsts_soffn, DSTS_SOFFN) USB_DWC2_GET_FIELD_DEFINE(dsts_enumspd, DSTS_ENUMSPD) /* Device all endpoints interrupt registers */ @@ -494,6 +591,34 @@ USB_DWC2_GET_FIELD_DEFINE(dsts_enumspd, DSTS_ENUMSPD) #define USB_DWC2_DAINT_OUTEPINT(ep_num) BIT(16UL + ep_num) #define USB_DWC2_DAINT_INEPINT(ep_num) BIT(ep_num) +/* Device threshold control register */ +#define USB_DWC2_DTHRCTL 0x0830UL +#define USB_DWC2_DTHRCTL_ARBPRKEN_POS 27UL +#define USB_DWC2_DTHRCTL_ARBPRKEN BIT(USB_DWC2_DTHRCTL_ARBPRKEN_POS) +#define USB_DWC2_DTHRCTL_RXTHRLEN_POS 17UL +#define USB_DWC2_DTHRCTL_RXTHRLEN_MASK (0x1FFUL << USB_DWC2_DTHRCTL_RXTHRLEN_POS) +#define USB_DWC2_DTHRCTL_RXTHREN_POS 16UL +#define USB_DWC2_DTHRCTL_RXTHREN BIT(USB_DWC2_DTHRCTL_RXTHREN_POS) +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_POS 11UL +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_MASK (0x3UL << USB_DWC2_DTHRCTL_AHBTHRRATIO_POS) +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESZERO 0 +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESONE 1 +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESTWO 2 +#define USB_DWC2_DTHRCTL_AHBTHRRATIO_THRESTHREE 3 +#define USB_DWC2_DTHRCTL_TXTHRLEN_POS 2UL +#define USB_DWC2_DTHRCTL_TXTHRLEN_MASK (0x1FFUL << USB_DWC2_DTHRCTL_TXTHRLEN_POS) +#define USB_DWC2_DTHRCTL_ISOTHREN_POS 1UL +#define USB_DWC2_DTHRCTL_ISOTHREN BIT(USB_DWC2_DTHRCTL_ISOTHREN_POS) +#define USB_DWC2_DTHRCTL_NONISOTHREN_POS 0UL +#define USB_DWC2_DTHRCTL_NONISOTHREN BIT(USB_DWC2_DTHRCTL_NONISOTHREN_POS) + +USB_DWC2_GET_FIELD_DEFINE(dthrctl_rxthrlen, DTHRCTL_RXTHRLEN) +USB_DWC2_GET_FIELD_DEFINE(dthrctl_ahbthrratio, DTHRCTL_AHBTHRRATIO) +USB_DWC2_GET_FIELD_DEFINE(dthrctl_txthrlen, DTHRCTL_TXTHRLEN) +USB_DWC2_SET_FIELD_DEFINE(dthrctl_rxthrlen, DTHRCTL_RXTHRLEN) +USB_DWC2_SET_FIELD_DEFINE(dthrctl_ahbthrratio, DTHRCTL_AHBTHRRATIO) +USB_DWC2_SET_FIELD_DEFINE(dthrctl_txthrlen, DTHRCTL_TXTHRLEN) + /* * Device IN/OUT endpoint control register * IN endpoint offsets 0x0900 + (0x20 * n), n = 0 .. x, @@ -508,8 +633,10 @@ USB_DWC2_GET_FIELD_DEFINE(dsts_enumspd, DSTS_ENUMSPD) #define USB_DWC2_DEPCTL_EPDIS BIT(USB_DWC2_DEPCTL_EPDIS_POS) #define USB_DWC2_DEPCTL_SETD1PID_POS 29UL #define USB_DWC2_DEPCTL_SETD1PID BIT(USB_DWC2_DEPCTL_SETD1PID_POS) +#define USB_DWC2_DEPCTL_SETODDFR USB_DWC2_DEPCTL_SETD1PID #define USB_DWC2_DEPCTL_SETD0PID_POS 28UL #define USB_DWC2_DEPCTL_SETD0PID BIT(USB_DWC2_DEPCTL_SETD0PID_POS) +#define USB_DWC2_DEPCTL_SETEVENFR USB_DWC2_DEPCTL_SETD0PID #define USB_DWC2_DEPCTL_SNAK_POS 27UL #define USB_DWC2_DEPCTL_SNAK BIT(USB_DWC2_DEPCTL_SNAK_POS) #define USB_DWC2_DEPCTL_CNAK_POS 26UL @@ -616,38 +743,67 @@ USB_DWC2_SET_FIELD_DEFINE(depctl_mps, DEPCTL_MPS) #define USB_DWC2_DOEPINT_XFERCOMPL_POS 0UL #define USB_DWC2_DOEPINT_XFERCOMPL BIT(USB_DWC2_DOEPINT_XFERCOMPL_POS) -/* - * Device IN/OUT control endpoint transfer size register - */ +/* Device IN control endpoint transfer size register */ #define USB_DWC2_DIEPTSIZ0 0x0910UL +#define USB_DWC2_DIEPTSIZ0_PKTCNT_POS 19UL +#define USB_DWC2_DIEPTSIZ0_PKTCNT_MASK (0x3UL << USB_DWC2_DIEPTSIZ0_PKTCNT_POS) +#define USB_DWC2_DIEPTSIZ0_XFERSIZE_POS 0UL +#define USB_DWC2_DIEPTSIZ0_XFERSIZE_MASK 0x7FUL + +USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_pktcnt, DIEPTSIZ0_PKTCNT) +USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_xfersize, DIEPTSIZ0_XFERSIZE) + +/* Device OUT control endpoint transfer size register */ #define USB_DWC2_DOEPTSIZ0 0x0B10UL #define USB_DWC2_DOEPTSIZ0_SUPCNT_POS 29UL #define USB_DWC2_DOEPTSIZ0_SUPCNT_MASK (0x3UL << USB_DWC2_DOEPTSIZ0_SUPCNT_POS) #define USB_DWC2_DOEPTSIZ0_PKTCNT_POS 19UL #define USB_DWC2_DOEPTSIZ0_PKTCNT_MASK (0x1UL << USB_DWC2_DOEPTSIZ0_PKTCNT_POS) -#define USB_DWC2_DIEPTSIZ0_PKTCNT_POS 19UL -#define USB_DWC2_DIEPTSIZ0_PKTCNT_MASK (0x3UL << USB_DWC2_DIEPTSIZ0_PKTCNT_POS) -#define USB_DWC2_DEPTSIZ0_XFERSIZE_POS 0UL -#define USB_DWC2_DEPTSIZ0_XFERSIZE_MASK 0x7FUL +#define USB_DWC2_DOEPTSIZ0_XFERSIZE_POS 0UL +#define USB_DWC2_DOEPTSIZ0_XFERSIZE_MASK 0x7FUL USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_supcnt, DOEPTSIZ0_SUPCNT) USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_pktcnt, DOEPTSIZ0_PKTCNT) -USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_xfersize, DEPTSIZ0_XFERSIZE) -USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_pktcnt, DIEPTSIZ0_PKTCNT) -USB_DWC2_GET_FIELD_DEFINE(dieptsiz0_xfersize, DEPTSIZ0_XFERSIZE) +USB_DWC2_GET_FIELD_DEFINE(doeptsiz0_xfersize, DOEPTSIZ0_XFERSIZE) /* - * Device IN/OUT endpoint transfer size register - * IN at offsets 0x0910 + (0x20 * n), n = 1 .. x, - * OUT at offsets 0x0B10 + (0x20 * n), n = 1 .. x + * Device IN endpoint transfer size register + * at offsets 0x0910 + (0x20 * n), n = 1 .. x */ -#define USB_DWC2_DEPTSIZN_PKTCNT_POS 19UL -#define USB_DWC2_DEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DEPTSIZN_PKTCNT_POS) -#define USB_DWC2_DEPTSIZN_XFERSIZE_POS 0UL -#define USB_DWC2_DEPTSIZN_XFERSIZE_MASK 0x7FFFFUL +#define USB_DWC2_DIEPTSIZN_MC_POS 29UL +#define USB_DWC2_DIEPTSIZN_MC_MASK (0x3UL << USB_DWC2_DIEPTSIZN_MC_POS) +#define USB_DWC2_DIEPTSIZN_PKTCNT_POS 19UL +#define USB_DWC2_DIEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DIEPTSIZN_PKTCNT_POS) +#define USB_DWC2_DIEPTSIZN_XFERSIZE_POS 0UL +#define USB_DWC2_DIEPTSIZN_XFERSIZE_MASK 0x7FFFFUL + +USB_DWC2_GET_FIELD_DEFINE(dieptsizn_mc, DIEPTSIZN_MC) +USB_DWC2_GET_FIELD_DEFINE(dieptsizn_pktcnt, DIEPTSIZN_PKTCNT) +USB_DWC2_GET_FIELD_DEFINE(dieptsizn_xfersize, DIEPTSIZN_XFERSIZE) +USB_DWC2_SET_FIELD_DEFINE(dieptsizn_mc, DIEPTSIZN_MC) +USB_DWC2_SET_FIELD_DEFINE(dieptsizn_pktcnt, DIEPTSIZN_PKTCNT) +USB_DWC2_SET_FIELD_DEFINE(dieptsizn_xfersize, DIEPTSIZN_XFERSIZE) -USB_DWC2_GET_FIELD_DEFINE(deptsizn_pktcnt, DEPTSIZN_PKTCNT) -USB_DWC2_GET_FIELD_DEFINE(deptsizn_xfersize, DEPTSIZN_XFERSIZE) +/* + * Device OUT endpoint transfer size register + * at offsets 0x0B10 + (0x20 * n), n = 1 .. x + */ +#define USB_DWC2_DOEPTSIZN_RXDPID_POS 29UL +#define USB_DWC2_DOEPTSIZN_RXDPID_MASK (0x3UL << USB_DWC2_DOEPTSIZN_RXDPID_POS) +#define USB_DWC2_DOEPTSIZN_RXDPID_MDATA 3 +#define USB_DWC2_DOEPTSIZN_RXDPID_DATA1 2 +#define USB_DWC2_DOEPTSIZN_RXDPID_DATA2 1 +#define USB_DWC2_DOEPTSIZN_RXDPID_DATA0 0 +#define USB_DWC2_DOEPTSIZN_PKTCNT_POS 19UL +#define USB_DWC2_DOEPTSIZN_PKTCNT_MASK (0x3FFUL << USB_DWC2_DOEPTSIZN_PKTCNT_POS) +#define USB_DWC2_DOEPTSIZN_XFERSIZE_POS 0UL +#define USB_DWC2_DOEPTSIZN_XFERSIZE_MASK 0x7FFFFUL + +USB_DWC2_GET_FIELD_DEFINE(doeptsizn_rxdpid, DOEPTSIZN_RXDPID) +USB_DWC2_GET_FIELD_DEFINE(doeptsizn_pktcnt, DOEPTSIZN_PKTCNT) +USB_DWC2_GET_FIELD_DEFINE(doeptsizn_xfersize, DOEPTSIZN_XFERSIZE) +USB_DWC2_SET_FIELD_DEFINE(doeptsizn_pktcnt, DOEPTSIZN_PKTCNT) +USB_DWC2_SET_FIELD_DEFINE(doeptsizn_xfersize, DOEPTSIZN_XFERSIZE) /* * Device IN/OUT endpoint transfer size register diff --git a/drivers/usb/udc/CMakeLists.txt b/drivers/usb/udc/CMakeLists.txt index ebca5f64277..6004729d373 100644 --- a/drivers/usb/udc/CMakeLists.txt +++ b/drivers/usb/udc/CMakeLists.txt @@ -12,3 +12,6 @@ zephyr_library_sources_ifdef(CONFIG_UDC_KINETIS udc_kinetis.c) zephyr_library_sources_ifdef(CONFIG_UDC_SKELETON udc_skeleton.c) zephyr_library_sources_ifdef(CONFIG_UDC_VIRTUAL udc_virtual.c) zephyr_library_sources_ifdef(CONFIG_UDC_STM32 udc_stm32.c) +zephyr_library_sources_ifdef(CONFIG_UDC_IT82XX2 udc_it82xx2.c) +zephyr_library_sources_ifdef(CONFIG_UDC_NXP_EHCI udc_mcux_ehci.c) +zephyr_library_sources_ifdef(CONFIG_UDC_NXP_IP3511 udc_mcux_ip3511.c) diff --git a/drivers/usb/udc/Kconfig b/drivers/usb/udc/Kconfig index 5b19a3385ad..bd6f71eb731 100644 --- a/drivers/usb/udc/Kconfig +++ b/drivers/usb/udc/Kconfig @@ -25,6 +25,13 @@ config UDC_BUF_POOL_SIZE help Total amount of memory available for UDC requests. +config UDC_BUF_FORCE_NOCACHE + bool "Place the buffer pools in the nocache memory region" + depends on NOCACHE_MEMORY && DCACHE + help + Place the buffer pools in the nocache memory region if the driver + cannot handle buffers in cached memory. + config UDC_WORKQUEUE bool "Use a dedicate work queue for UDC drivers" help @@ -53,5 +60,7 @@ source "drivers/usb/udc/Kconfig.kinetis" source "drivers/usb/udc/Kconfig.skeleton" source "drivers/usb/udc/Kconfig.virtual" source "drivers/usb/udc/Kconfig.stm32" +source "drivers/usb/udc/Kconfig.it82xx2" +source "drivers/usb/udc/Kconfig.mcux" endif # UDC_DRIVER diff --git a/drivers/usb/udc/Kconfig.dwc2 b/drivers/usb/udc/Kconfig.dwc2 index a1a1c9d22ce..fc38e07e60d 100644 --- a/drivers/usb/udc/Kconfig.dwc2 +++ b/drivers/usb/udc/Kconfig.dwc2 @@ -10,6 +10,13 @@ config UDC_DWC2 help DWC2 USB device controller driver. +config UDC_DWC2_DMA + bool "DWC2 USB DMA support" + default y + depends on UDC_DWC2 + help + Enable Buffer DMA if DWC2 USB controller supports Internal DMA. + config UDC_DWC2_STACK_SIZE int "UDC DWC2 driver internal thread stack size" depends on UDC_DWC2 diff --git a/drivers/usb/udc/Kconfig.it82xx2 b/drivers/usb/udc/Kconfig.it82xx2 new file mode 100644 index 00000000000..649b2531d10 --- /dev/null +++ b/drivers/usb/udc/Kconfig.it82xx2 @@ -0,0 +1,27 @@ +# Copyright (c) 2023 ITE Corporation. +# SPDX-License-Identifier: Apache-2.0 + +config UDC_IT82XX2 + bool "IT82XX2 USB device controller driver" + default y + depends on DT_HAS_ITE_IT82XX2_USB_ENABLED + help + IT82xx2 USB device controller driver. + +if UDC_IT82XX2 + +config UDC_IT82xx2_EVENT_COUNT + int "UDC IT82xx2 event count" + range 4 64 + default 8 + help + IT82xx2 event count. + +config UDC_IT82xx2_STACK_SIZE + int "IT82xx2 UDC driver internal thread stack size" + default 1024 + help + Size of the stack used in the driver for IT82xx2 USBD ISR event + handling. + +endif # UDC_IT82XX2 diff --git a/drivers/usb/udc/Kconfig.mcux b/drivers/usb/udc/Kconfig.mcux new file mode 100644 index 00000000000..590e772600e --- /dev/null +++ b/drivers/usb/udc/Kconfig.mcux @@ -0,0 +1,18 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config UDC_NXP_EHCI + bool "NXP MCUX USB EHCI Device controller driver" + default y + depends on DT_HAS_NXP_EHCI_ENABLED + select NOCACHE_MEMORY if HAS_MCUX_CACHE && CPU_HAS_DCACHE + imply UDC_BUF_FORCE_NOCACHE + help + NXP MCUX USB Device Controller Driver for EHCI. + +config UDC_NXP_IP3511 + bool "NXP MCUX USB IP3511 Device controller driver" + default y + depends on DT_HAS_NXP_LPCIP3511_ENABLED + help + NXP MCUX USB Device Controller Driver for KHCI. diff --git a/drivers/usb/udc/udc_common.c b/drivers/usb/udc/udc_common.c index eeed077ebc4..6ef33cc1d30 100644 --- a/drivers/usb/udc/udc_common.c +++ b/drivers/usb/udc/udc_common.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "udc_common.h" #include @@ -20,9 +21,39 @@ #endif LOG_MODULE_REGISTER(udc, CONFIG_UDC_DRIVER_LOG_LEVEL); +static inline uint8_t *udc_pool_data_alloc(struct net_buf *const buf, + size_t *const size, k_timeout_t timeout) +{ + struct net_buf_pool *const buf_pool = net_buf_pool_get(buf->pool_id); + struct k_heap *const pool = buf_pool->alloc->alloc_data; + void *b; + + *size = ROUND_UP(*size, UDC_BUF_GRANULARITY); + b = k_heap_aligned_alloc(pool, UDC_BUF_ALIGN, *size, timeout); + if (b == NULL) { + *size = 0; + return NULL; + } + + return b; +} + +static inline void udc_pool_data_unref(struct net_buf *buf, uint8_t *const data) +{ + struct net_buf_pool *buf_pool = net_buf_pool_get(buf->pool_id); + struct k_heap *pool = buf_pool->alloc->alloc_data; + + k_heap_free(pool, data); +} + +const struct net_buf_data_cb net_buf_dma_cb = { + .alloc = udc_pool_data_alloc, + .unref = udc_pool_data_unref, +}; + static inline void udc_buf_destroy(struct net_buf *buf); -NET_BUF_POOL_VAR_DEFINE(udc_ep_pool, +UDC_BUF_POOL_VAR_DEFINE(udc_ep_pool, CONFIG_UDC_BUF_COUNT, CONFIG_UDC_BUF_POOL_SIZE, sizeof(struct udc_buf_info), udc_buf_destroy); @@ -231,7 +262,7 @@ static bool ep_check_config(const struct device *dev, return false; } - if (mps > cfg->caps.mps) { + if (USB_MPS_EP_SIZE(mps) > USB_MPS_EP_SIZE(cfg->caps.mps)) { return false; } @@ -242,12 +273,16 @@ static bool ep_check_config(const struct device *dev, } break; case USB_EP_TYPE_INTERRUPT: - if (!cfg->caps.interrupt) { + if (!cfg->caps.interrupt || + (USB_MPS_ADDITIONAL_TRANSACTIONS(mps) && + !cfg->caps.high_bandwidth)) { return false; } break; case USB_EP_TYPE_ISO: - if (!cfg->caps.iso) { + if (!cfg->caps.iso || + (USB_MPS_ADDITIONAL_TRANSACTIONS(mps) && + !cfg->caps.high_bandwidth)) { return false; } break; diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index e97001ca9c3..1a780ac920f 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,6 @@ enum dwc2_drv_event_type { struct dwc2_drv_event { const struct device *dev; enum dwc2_drv_event_type type; - uint32_t bcnt; uint8_t ep; }; @@ -49,7 +49,14 @@ K_MSGQ_DEFINE(drv_msgq, sizeof(struct dwc2_drv_event), * of 512 bytes. The value must be adjusted according to the number of OUT * endpoints. */ -#define UDC_DWC2_GRXFSIZ_DEFAULT (15U + 512U/4U) +#define UDC_DWC2_GRXFSIZ_FS_DEFAULT (15U + 512U/4U) +/* Default Rx FIFO size in 32-bit words calculated to support High-Speed with: + * * 1 control endpoint in Completer/Buffer DMA mode: 13 locations + * * Global OUT NAK: 1 location + * * Space for 3 * 1024 packets: ((1024/4) + 1) * 3 = 774 locations + * Driver adds 2 locations for each OUT endpoint to this value. + */ +#define UDC_DWC2_GRXFSIZ_HS_DEFAULT (13 + 1 + 774) /* TX FIFO0 depth in 32-bit words (used by control IN endpoint) */ #define UDC_DWC2_FIFO0_DEPTH 16U @@ -63,12 +70,16 @@ struct udc_dwc2_data { uint32_t ghwcfg1; uint32_t enumspd; uint32_t txf_set; - uint32_t grxfsiz; - uint32_t dfifodepth; uint32_t max_xfersize; uint32_t max_pktcnt; uint32_t tx_len[16]; + uint32_t rx_siz[16]; + uint16_t dfifodepth; + uint16_t rxfifo_depth; + uint16_t max_txfifo_depth[16]; + uint16_t sof_num; unsigned int dynfifosizing : 1; + unsigned int bufferdma : 1; /* Number of endpoints including control endpoint */ uint8_t numdeveps; /* Number of IN endpoints including control endpoint */ @@ -78,6 +89,9 @@ struct udc_dwc2_data { uint8_t setup[8]; }; +static void udc_dwc2_ep_disable(const struct device *dev, + struct udc_ep_config *const cfg, bool stall); + #if defined(CONFIG_PINCTRL) #include @@ -174,16 +188,12 @@ static void dwc2_flush_rx_fifo(const struct device *dev) } } -static void dwc2_flush_tx_fifo(const struct device *dev, const uint8_t idx) +static void dwc2_flush_tx_fifo(const struct device *dev, const uint8_t fnum) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); mem_addr_t grstctl_reg = (mem_addr_t)&base->grstctl; - /* TODO: use dwc2_get_dxepctl_reg() */ - mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[idx].diepctl; uint32_t grstctl; - uint32_t fnum; - fnum = usb_dwc2_get_depctl_txfnum(sys_read32(diepctl_reg)); grstctl = usb_dwc2_set_grstctl_txfnum(fnum) | USB_DWC2_GRSTCTL_TXFFLSH; sys_write32(grstctl, grstctl_reg); @@ -248,6 +258,42 @@ static void dwc2_set_epint(const struct device *dev, } } +static bool dwc2_ep_is_periodic(struct udc_ep_config *const cfg) +{ + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_INTERRUPT: + __fallthrough; + case USB_EP_TYPE_ISO: + return true; + default: + return false; + } +} + +static bool dwc2_ep_is_iso(struct udc_ep_config *const cfg) +{ + return (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) == USB_EP_TYPE_ISO; +} + +static bool dwc2_dma_buffer_ok_to_use(const struct device *dev, void *buf, + uint32_t xfersize, uint16_t mps) +{ + ARG_UNUSED(dev); + + if (!IS_ALIGNED(buf, 4)) { + LOG_ERR("Buffer not aligned"); + return false; + } + + /* We can only do 1 packet if Max Packet Size is not multiple of 4 */ + if (unlikely(mps % 4) && (xfersize > USB_MPS_EP_SIZE(mps))) { + LOG_ERR("Padding not supported"); + return false; + } + + return true; +} + /* Can be called from ISR context */ static int dwc2_tx_fifo_write(const struct device *dev, struct udc_ep_config *const cfg, struct net_buf *const buf) @@ -261,28 +307,53 @@ static int dwc2_tx_fifo_write(const struct device *dev, mem_addr_t diepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; mem_addr_t diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint; - uint32_t max_xfersize, max_pktcnt, pktcnt, spcavail; + uint32_t diepctl; + uint32_t max_xfersize, max_pktcnt, pktcnt; + const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); const size_t d = sizeof(uint32_t); unsigned int key; uint32_t len; + const bool is_periodic = dwc2_ep_is_periodic(cfg); + const bool is_iso = dwc2_ep_is_iso(cfg); - spcavail = dwc2_ftx_avail(dev, ep_idx); - /* Round down to multiple of endpoint MPS */ - spcavail -= spcavail % cfg->mps; - /* - * Here, the available space should be equal to the FIFO space - * assigned/configured for that endpoint because we do not schedule another - * transfer until the previous one has not finished. For simplicity, - * we only check that the available space is not less than the endpoint - * MPS. - */ - if (spcavail < cfg->mps) { - LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)", - cfg->addr, spcavail, dwc2_ftx_avail(dev, ep_idx)); - return -EAGAIN; + if (is_iso) { + /* Isochronous transfers can only be programmed one + * (micro-)frame at a time. + */ + len = MIN(buf->len, USB_MPS_TO_TPL(cfg->mps)); + } else { + /* DMA automatically handles packet split. In completer mode, + * the value is sanitized below. + */ + len = buf->len; } - len = MIN(buf->len, spcavail); + if (!priv->bufferdma) { + uint32_t spcavail = dwc2_ftx_avail(dev, ep_idx); + uint32_t spcperpkt = ROUND_UP(udc_mps_ep_size(cfg), 4); + uint32_t max_pkts, max_transfer; + + /* Maximum number of packets that can fit in TxFIFO */ + max_pkts = spcavail / spcperpkt; + + /* We can transfer up to max_pkts MPS packets and a short one */ + max_transfer = (max_pkts * udc_mps_ep_size(cfg)) + + (spcavail % spcperpkt); + + /* If there is enough space for the transfer, there's no need + * to check any additional conditions. If the transfer is larger + * than TxFIFO then TxFIFO must be able to hold at least one + * packet (for periodic transfers at least the number of packets + * per microframe). + */ + if ((len > max_transfer) && ((1 + addnl) > max_pkts)) { + LOG_ERR("ep 0x%02x FIFO space is too low, %u (%u)", + cfg->addr, spcavail, len); + return -EAGAIN; + } + + len = MIN(len, max_transfer); + } if (len != 0U) { max_pktcnt = dwc2_get_iept_pktctn(dev, ep_idx); @@ -293,54 +364,101 @@ static int dwc2_tx_fifo_write(const struct device *dev, * Avoid short packets if the transfer size cannot be * handled in one set. */ - len = ROUND_DOWN(max_xfersize, cfg->mps); + len = ROUND_DOWN(max_xfersize, USB_MPS_TO_TPL(cfg->mps)); } /* * Determine the number of packets for the current transfer; * if the pktcnt is too large, truncate the actual transfer length. */ - pktcnt = DIV_ROUND_UP(len, cfg->mps); + pktcnt = DIV_ROUND_UP(len, udc_mps_ep_size(cfg)); if (pktcnt > max_pktcnt) { - pktcnt = max_pktcnt; - len = pktcnt * cfg->mps; + pktcnt = ROUND_DOWN(max_pktcnt, (1 + addnl)); + len = pktcnt * udc_mps_ep_size(cfg); } } else { /* ZLP */ pktcnt = 1U; } - LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u spcavail %u", - cfg->addr, len, pktcnt, spcavail); + LOG_DBG("Prepare ep 0x%02x xfer len %u pktcnt %u addnl %u", + cfg->addr, len, pktcnt, addnl); priv->tx_len[ep_idx] = len; /* Lock and write to endpoint FIFO */ key = irq_lock(); /* Set number of packets and transfer size */ - sys_write32((pktcnt << USB_DWC2_DEPTSIZN_PKTCNT_POS) | len, dieptsiz_reg); + sys_write32((is_periodic ? usb_dwc2_set_dieptsizn_mc(1 + addnl) : 0) | + usb_dwc2_set_dieptsizn_pktcnt(pktcnt) | + usb_dwc2_set_dieptsizn_xfersize(len), dieptsiz_reg); + + if (priv->bufferdma) { + if (!dwc2_dma_buffer_ok_to_use(dev, buf->data, len, cfg->mps)) { + /* Cannot continue unless buffer is bounced. Device will + * cease to function. Is fatal error appropriate here? + */ + irq_unlock(key); + return -ENOTSUP; + } + + sys_write32((uint32_t)buf->data, + (mem_addr_t)&base->in_ep[ep_idx].diepdma); + + sys_cache_data_flush_range(buf->data, len); + } + + diepctl = sys_read32(diepctl_reg); + if (!(diepctl & USB_DWC2_DEPCTL_USBACTEP)) { + /* Do not attempt to write data on inactive endpoint, because + * no fifo is assigned to inactive endpoint and therefore it is + * possible that the write will corrupt other endpoint fifo. + */ + irq_unlock(key); + return -ENOENT; + } + + if (is_iso) { + if (priv->sof_num & 1) { + diepctl |= USB_DWC2_DEPCTL_SETODDFR; + } else { + diepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } + } /* Clear NAK and set endpoint enable */ - sys_set_bits(diepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK); + diepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK; + sys_write32(diepctl, diepctl_reg); + /* Clear IN Endpoint NAK Effective interrupt in case it was set */ sys_write32(USB_DWC2_DIEPINT_INEPNAKEFF, diepint_reg); - /* FIFO access is always in 32-bit words */ + if (!priv->bufferdma) { + const uint8_t *src = buf->data; - for (uint32_t i = 0UL; i < len; i += d) { - uint32_t val = buf->data[i]; + while (pktcnt > 0) { + uint32_t pktlen = MIN(len, udc_mps_ep_size(cfg)); - if (i + 1 < len) { - val |= ((uint32_t)buf->data[i + 1UL]) << 8; - } - if (i + 2 < len) { - val |= ((uint32_t)buf->data[i + 2UL]) << 16; - } - if (i + 3 < len) { - val |= ((uint32_t)buf->data[i + 3UL]) << 24; - } + for (uint32_t i = 0UL; i < pktlen; i += d) { + uint32_t val = src[i]; - sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx)); + if (i + 1 < pktlen) { + val |= ((uint32_t)src[i + 1UL]) << 8; + } + if (i + 2 < pktlen) { + val |= ((uint32_t)src[i + 2UL]) << 16; + } + if (i + 3 < pktlen) { + val |= ((uint32_t)src[i + 3UL]) << 24; + } + + sys_write32(val, UDC_DWC2_EP_FIFO(base, ep_idx)); + } + + pktcnt--; + src += pktlen; + len -= pktlen; + } } irq_unlock(key); @@ -381,28 +499,76 @@ static inline int dwc2_read_fifo(const struct device *dev, const uint8_t ep, } /* Can be called from ISR and we call it only when there is a buffer in the queue */ -static void dwc2_prep_rx(const struct device *dev, - struct udc_ep_config *const cfg, const bool ncnak) +static void dwc2_prep_rx(const struct device *dev, struct net_buf *buf, + struct udc_ep_config *const cfg) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct udc_dwc2_data *const priv = udc_get_private(dev); uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); mem_addr_t doeptsiz_reg = (mem_addr_t)&base->out_ep[ep_idx].doeptsiz; mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, ep_idx); + uint32_t pktcnt; uint32_t doeptsiz; + uint32_t doepctl; + uint32_t xfersize; + + /* Clear NAK and set endpoint enable */ + doepctl = sys_read32(doepctl_reg); + doepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK; - doeptsiz = (1 << USB_DWC2_DOEPTSIZ0_PKTCNT_POS) | cfg->mps; + if (dwc2_ep_is_iso(cfg)) { + xfersize = USB_MPS_TO_TPL(cfg->mps); + pktcnt = 1 + USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); + + if (xfersize > net_buf_tailroom(buf)) { + LOG_ERR("ISO RX buffer too small"); + return; + } + + /* Set the Even/Odd (micro-)frame appropriately */ + if (priv->sof_num & 1) { + doepctl |= USB_DWC2_DEPCTL_SETEVENFR; + } else { + doepctl |= USB_DWC2_DEPCTL_SETODDFR; + } + } else { + xfersize = net_buf_tailroom(buf); + + /* Do as many packets in a single transfer as possible */ + if (xfersize > priv->max_xfersize) { + xfersize = ROUND_DOWN(priv->max_xfersize, USB_MPS_TO_TPL(cfg->mps)); + } + + pktcnt = DIV_ROUND_UP(xfersize, USB_MPS_EP_SIZE(cfg->mps)); + } + + pktcnt = DIV_ROUND_UP(xfersize, udc_mps_ep_size(cfg)); + doeptsiz = usb_dwc2_set_doeptsizn_pktcnt(pktcnt) | + usb_dwc2_set_doeptsizn_xfersize(xfersize); if (cfg->addr == USB_CONTROL_EP_OUT) { - doeptsiz |= (3 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); + /* Use 1 to allow 8 byte long buffers for SETUP data */ + doeptsiz |= (1 << USB_DWC2_DOEPTSIZ0_SUPCNT_POS); } + priv->rx_siz[ep_idx] = doeptsiz; sys_write32(doeptsiz, doeptsiz_reg); - if (ncnak) { - sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA); - } else { - sys_set_bits(doepctl_reg, USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_CNAK); + if (priv->bufferdma) { + if (!dwc2_dma_buffer_ok_to_use(dev, buf->data, xfersize, cfg->mps)) { + /* Cannot continue unless buffer is bounced. Device will + * cease to function. Is fatal error appropriate here? + */ + return; + } + + sys_write32((uint32_t)buf->data, + (mem_addr_t)&base->out_ep[ep_idx].doepdma); + + sys_cache_data_invd_range(buf->data, xfersize); } + sys_write32(doepctl, doepctl_reg); + LOG_INF("Prepare RX 0x%02x doeptsiz 0x%x", cfg->addr, doeptsiz); } @@ -417,7 +583,7 @@ static void dwc2_handle_xfer_next(const struct device *dev, } if (USB_EP_DIR_IS_OUT(cfg->addr)) { - dwc2_prep_rx(dev, cfg, 0); + dwc2_prep_rx(dev, buf, cfg); } else { if (dwc2_tx_fifo_write(dev, cfg, buf)) { LOG_ERR("Failed to start write to TX FIFO, ep 0x%02x", @@ -430,6 +596,7 @@ static void dwc2_handle_xfer_next(const struct device *dev, static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length) { + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); struct net_buf *buf; buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); @@ -437,8 +604,8 @@ static int dwc2_ctrl_feed_dout(const struct device *dev, const size_t length) return -ENOMEM; } - udc_buf_put(udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), buf); - dwc2_prep_rx(dev, udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT), 0); + udc_buf_put(ep_cfg, buf); + dwc2_prep_rx(dev, buf, ep_cfg); LOG_DBG("feed buf %p", buf); return 0; @@ -507,7 +674,7 @@ static inline int dwc2_handle_evt_dout(const struct device *dev, buf = udc_buf_get(dev, cfg->addr); if (buf == NULL) { - LOG_ERR("No buffer queued for control ep"); + LOG_ERR("No buffer queued for ep 0x%02x", cfg->addr); return -ENODATA; } @@ -642,6 +809,7 @@ static void dwc2_on_bus_reset(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); struct udc_dwc2_data *const priv = udc_get_private(dev); + uint32_t doepmsk; /* Set the NAK bit for all OUT endpoints */ for (uint8_t i = 0U; i < priv->numdeveps; i++) { @@ -656,10 +824,20 @@ static void dwc2_on_bus_reset(const struct device *dev) } } - sys_write32(USB_DWC2_DOEPINT_SETUP, (mem_addr_t)&base->doepmsk); - sys_set_bits((mem_addr_t)&base->gintmsk, USB_DWC2_GINTSTS_RXFLVL); + doepmsk = USB_DWC2_DOEPINT_SETUP | USB_DWC2_DOEPINT_XFERCOMPL; + if (priv->bufferdma) { + doepmsk |= USB_DWC2_DOEPINT_STSPHSERCVD; + } + + sys_write32(doepmsk, (mem_addr_t)&base->doepmsk); sys_set_bits((mem_addr_t)&base->diepmsk, USB_DWC2_DIEPINT_XFERCOMPL); + /* Software has to handle RxFLvl interrupt only in Completer mode */ + if (!priv->bufferdma) { + sys_set_bits((mem_addr_t)&base->gintmsk, + USB_DWC2_GINTSTS_RXFLVL); + } + /* Clear device address during reset. */ sys_clear_bits((mem_addr_t)&base->dcfg, USB_DWC2_DCFG_DEVADDR_MASK); } @@ -712,43 +890,30 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); struct udc_ep_config *ep_cfg; - struct dwc2_drv_event evt; struct net_buf *buf; uint32_t grxstsp; uint32_t pktsts; + uint32_t bcnt; + uint8_t ep; grxstsp = sys_read32((mem_addr_t)&base->grxstsp); - evt.ep = usb_dwc2_get_grxstsp_epnum(grxstsp); - evt.bcnt = usb_dwc2_get_grxstsp_bcnt(grxstsp); + ep = usb_dwc2_get_grxstsp_epnum(grxstsp); + bcnt = usb_dwc2_get_grxstsp_bcnt(grxstsp); pktsts = usb_dwc2_get_grxstsp_pktsts(grxstsp); - LOG_DBG("ep 0x%02x: pktsts %u, bcnt %u", evt.ep, pktsts, evt.bcnt); + LOG_DBG("ep 0x%02x: pktsts %u, bcnt %u", ep, pktsts, bcnt); switch (pktsts) { case USB_DWC2_GRXSTSR_PKTSTS_SETUP: - dwc2_read_fifo_setup(dev, evt.ep, evt.bcnt); + dwc2_read_fifo_setup(dev, ep, bcnt); break; case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA: - evt.type = DWC2_DRV_EVT_DOUT; - ep_cfg = udc_get_ep_cfg(dev, evt.ep); + ep_cfg = udc_get_ep_cfg(dev, ep); buf = udc_buf_peek(dev, ep_cfg->addr); /* RxFIFO data must be retrieved even when buf is NULL */ - dwc2_read_fifo(dev, evt.ep, buf, evt.bcnt); - - if (buf == NULL) { - LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr); - udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); - break; - } - - if (net_buf_tailroom(buf) && evt.bcnt == ep_cfg->mps) { - dwc2_prep_rx(dev, ep_cfg, 0); - } else { - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); - } - + dwc2_read_fifo(dev, ep, buf, bcnt); break; case USB_DWC2_GRXSTSR_PKTSTS_OUT_DATA_DONE: LOG_DBG("RX pktsts DONE"); @@ -763,8 +928,8 @@ static inline void dwc2_handle_rxflvl(const struct device *dev) } } -static inline void dwc2_handle_xfercompl(const struct device *dev, - const uint8_t ep_idx) +static inline void dwc2_handle_in_xfercompl(const struct device *dev, + const uint8_t ep_idx) { struct udc_dwc2_data *const priv = udc_get_private(dev); struct udc_ep_config *ep_cfg; @@ -814,7 +979,7 @@ static inline void dwc2_handle_iepint(const struct device *dev) n | USB_EP_DIR_IN, status); if (status & USB_DWC2_DIEPINT_XFERCOMPL) { - dwc2_handle_xfercompl(dev, n); + dwc2_handle_in_xfercompl(dev, n); } } @@ -824,9 +989,85 @@ static inline void dwc2_handle_iepint(const struct device *dev) sys_write32(USB_DWC2_GINTSTS_IEPINT, (mem_addr_t)&base->gintsts); } +static inline void dwc2_handle_out_xfercompl(const struct device *dev, + const uint8_t ep_idx) +{ + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep_idx); + struct udc_dwc2_data *const priv = udc_get_private(dev); + struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct dwc2_drv_event evt; + uint32_t bcnt; + struct net_buf *buf; + uint32_t doeptsiz; + const bool is_iso = dwc2_ep_is_iso(ep_cfg); + + doeptsiz = sys_read32((mem_addr_t)&base->out_ep[ep_idx].doeptsiz); + + buf = udc_buf_peek(dev, ep_cfg->addr); + if (!buf) { + LOG_ERR("No buffer for ep 0x%02x", ep_cfg->addr); + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return; + } + + evt.type = DWC2_DRV_EVT_DOUT; + evt.ep = ep_cfg->addr; + + /* The original transfer size value is necessary here because controller + * decreases the value for every byte stored. + */ + bcnt = usb_dwc2_get_doeptsizn_xfersize(priv->rx_siz[ep_idx]) - + usb_dwc2_get_doeptsizn_xfersize(doeptsiz); + + if (is_iso) { + uint32_t pkts; + bool valid; + + pkts = usb_dwc2_get_doeptsizn_pktcnt(priv->rx_siz[ep_idx]) - + usb_dwc2_get_doeptsizn_pktcnt(doeptsiz); + switch (usb_dwc2_get_doeptsizn_rxdpid(doeptsiz)) { + case USB_DWC2_DOEPTSIZN_RXDPID_DATA0: + valid = (pkts == 1); + break; + case USB_DWC2_DOEPTSIZN_RXDPID_DATA1: + valid = (pkts == 2); + break; + case USB_DWC2_DOEPTSIZN_RXDPID_DATA2: + valid = (pkts == 3); + break; + case USB_DWC2_DOEPTSIZN_RXDPID_MDATA: + default: + valid = false; + break; + } + + if (!valid) { + if (!priv->bufferdma) { + /* RxFlvl added data to net buf, rollback */ + net_buf_remove_mem(buf, bcnt); + } + /* Data is not valid, discard it */ + bcnt = 0; + } + } + + if (priv->bufferdma && bcnt) { + sys_cache_data_invd_range(buf->data, bcnt); + net_buf_add(buf, bcnt); + } + + if (!is_iso && (bcnt % udc_mps_ep_size(ep_cfg)) == 0 && + net_buf_tailroom(buf)) { + dwc2_prep_rx(dev, buf, ep_cfg); + } else { + k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + } +} + static inline void dwc2_handle_oepint(const struct device *dev) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); + struct udc_dwc2_data *const priv = udc_get_private(dev); const uint8_t n_max = 16; uint32_t doepmsk; uint32_t daint; @@ -839,23 +1080,58 @@ static inline void dwc2_handle_oepint(const struct device *dev) uint32_t doepint; uint32_t status; - if (daint & USB_DWC2_DAINT_OUTEPINT(n)) { - /* Read and clear interrupt status */ - doepint = sys_read32(doepint_reg); - status = doepint & doepmsk; - sys_write32(status, doepint_reg); + if (!(daint & USB_DWC2_DAINT_OUTEPINT(n))) { + continue; + } - LOG_DBG("ep 0x%02x interrupt status: 0x%x", n, status); + /* Read and clear interrupt status */ + doepint = sys_read32(doepint_reg); + status = doepint & doepmsk; + sys_write32(status, doepint_reg); - if (status & USB_DWC2_DOEPINT_SETUP) { - struct dwc2_drv_event evt = { - .type = DWC2_DRV_EVT_SETUP, - .ep = USB_CONTROL_EP_OUT, - .bcnt = 8, - }; + LOG_DBG("ep 0x%02x interrupt status: 0x%x", n, status); - k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); - } + /* StupPktRcvd is not enabled for interrupt, but must be checked + * when XferComp hits to determine if SETUP token was received. + */ + if (priv->bufferdma && (status & USB_DWC2_DOEPINT_XFERCOMPL) && + (doepint & USB_DWC2_DOEPINT_STUPPKTRCVD)) { + uint32_t addr; + + sys_write32(USB_DWC2_DOEPINT_STUPPKTRCVD, doepint_reg); + status &= ~USB_DWC2_DOEPINT_XFERCOMPL; + + /* DMAAddr points past the memory location where the + * SETUP data was stored. Copy the received SETUP data + * to temporary location used also in Completer mode + * which allows common SETUP interrupt handling. + */ + addr = sys_read32((mem_addr_t)&base->out_ep[0].doepdma); + sys_cache_data_invd_range((void *)(addr - 8), 8); + memcpy(priv->setup, (void *)(addr - 8), sizeof(priv->setup)); + } + + if (status & USB_DWC2_DOEPINT_SETUP) { + struct dwc2_drv_event evt = { + .type = DWC2_DRV_EVT_SETUP, + .ep = USB_CONTROL_EP_OUT, + }; + + k_msgq_put(&drv_msgq, &evt, K_NO_WAIT); + } + + if (status & USB_DWC2_DOEPINT_STSPHSERCVD) { + /* Driver doesn't need any special handling, but it is + * mandatory that the bit is cleared in Buffer DMA mode. + * If the bit is not cleared (i.e. when this interrupt + * bit is masked), then SETUP interrupts will cease + * after first control transfer with data stage from + * device to host. + */ + } + + if (status & USB_DWC2_DOEPINT_XFERCOMPL) { + dwc2_handle_out_xfercompl(dev, n); } } @@ -863,10 +1139,112 @@ static inline void dwc2_handle_oepint(const struct device *dev) sys_write32(USB_DWC2_GINTSTS_OEPINT, (mem_addr_t)&base->gintsts); } +/* In DWC2 otg context incomplete isochronous IN transfer means that the host + * did not issue IN token to at least one isochronous endpoint and software has + * find on which endpoints the data is no longer valid and discard it. + */ +static void dwc2_handle_incompisoin(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); + mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts; + const uint32_t mask = + USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPTYPE_MASK | + USB_DWC2_DEPCTL_USBACTEP; + const uint32_t val = + USB_DWC2_DEPCTL_EPENA | + usb_dwc2_set_depctl_eptype(USB_DWC2_DEPCTL_EPTYPE_ISO) | + USB_DWC2_DEPCTL_USBACTEP; + + for (uint8_t i = 1U; i < priv->numdeveps; i++) { + uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i); + + if (epdir == USB_DWC2_GHWCFG1_EPDIR_IN || + epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) { + mem_addr_t diepctl_reg = dwc2_get_dxepctl_reg(dev, i | USB_EP_DIR_IN); + uint32_t diepctl; + + diepctl = sys_read32(diepctl_reg); + + /* Check if endpoint didn't receive ISO OUT data */ + if ((diepctl & mask) == val) { + struct udc_ep_config *cfg; + struct net_buf *buf; + + cfg = udc_get_ep_cfg(dev, i | USB_EP_DIR_IN); + __ASSERT_NO_MSG(cfg && cfg->stat.enabled && + dwc2_ep_is_iso(cfg)); + + udc_dwc2_ep_disable(dev, cfg, false); + + buf = udc_buf_get(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, 0); + } + } + } + } + + sys_write32(USB_DWC2_GINTSTS_INCOMPISOIN, gintsts_reg); +} + +/* In DWC2 otg context incomplete isochronous OUT transfer means that the host + * did not issue OUT token to at least one isochronous endpoint and software has + * to find on which endpoint it didn't receive any data and let the stack know. + */ +static void dwc2_handle_incompisoout(const struct device *dev) +{ + const struct udc_dwc2_config *const config = dev->config; + struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); + mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts; + const uint32_t mask = + USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_EPTYPE_MASK | + USB_DWC2_DEPCTL_DPID | USB_DWC2_DEPCTL_USBACTEP; + const uint32_t val = + USB_DWC2_DEPCTL_EPENA | + usb_dwc2_set_depctl_eptype(USB_DWC2_DEPCTL_EPTYPE_ISO) | + ((priv->sof_num & 1) ? USB_DWC2_DEPCTL_DPID : 0) | + USB_DWC2_DEPCTL_USBACTEP; + + for (uint8_t i = 1U; i < priv->numdeveps; i++) { + uint32_t epdir = usb_dwc2_get_ghwcfg1_epdir(priv->ghwcfg1, i); + + if (epdir == USB_DWC2_GHWCFG1_EPDIR_OUT || + epdir == USB_DWC2_GHWCFG1_EPDIR_BDIR) { + mem_addr_t doepctl_reg = dwc2_get_dxepctl_reg(dev, i); + uint32_t doepctl; + + doepctl = sys_read32(doepctl_reg); + + /* Check if endpoint didn't receive ISO OUT data */ + if ((doepctl & mask) == val) { + struct udc_ep_config *cfg; + struct net_buf *buf; + + cfg = udc_get_ep_cfg(dev, i); + __ASSERT_NO_MSG(cfg && cfg->stat.enabled && + dwc2_ep_is_iso(cfg)); + + udc_dwc2_ep_disable(dev, cfg, false); + + buf = udc_buf_get(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, 0); + } + } + } + } + + sys_write32(USB_DWC2_GINTSTS_INCOMPISOOUT, gintsts_reg); +} + static void udc_dwc2_isr_handler(const struct device *dev) { const struct udc_dwc2_config *const config = dev->config; struct usb_dwc2_reg *const base = config->base; + struct udc_dwc2_data *const priv = udc_get_private(dev); mem_addr_t gintsts_reg = (mem_addr_t)&base->gintsts; uint32_t int_status; uint32_t gintmsk; @@ -879,8 +1257,13 @@ static void udc_dwc2_isr_handler(const struct device *dev) LOG_DBG("GINTSTS 0x%x", int_status); if (int_status & USB_DWC2_GINTSTS_SOF) { + uint32_t dsts; + /* Clear USB SOF interrupt. */ sys_write32(USB_DWC2_GINTSTS_SOF, gintsts_reg); + + dsts = sys_read32((mem_addr_t)&base->dsts); + priv->sof_num = usb_dwc2_get_dsts_soffn(dsts); udc_submit_event(dev, UDC_EVT_SOF, 0); } @@ -926,6 +1309,14 @@ static void udc_dwc2_isr_handler(const struct device *dev) /* Handle OUT Endpoints interrupt */ dwc2_handle_oepint(dev); } + + if (int_status & USB_DWC2_GINTSTS_INCOMPISOIN) { + dwc2_handle_incompisoin(dev); + } + + if (int_status & USB_DWC2_GINTSTS_INCOMPISOOUT) { + dwc2_handle_incompisoout(dev); + } } (void)dwc2_quirk_irq_clear(dev); @@ -960,6 +1351,8 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, { struct udc_dwc2_data *const priv = udc_get_private(dev); uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + const uint32_t addnl = USB_MPS_ADDITIONAL_TRANSACTIONS(cfg->mps); + uint32_t reqdep; uint32_t txfaddr; uint32_t txfdep; uint32_t tmp; @@ -967,6 +1360,14 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, /* Keep everything but FIFO number */ tmp = *diepctl & ~USB_DWC2_DEPCTL_TXFNUM_MASK; + reqdep = DIV_ROUND_UP(udc_mps_ep_size(cfg), 4U); + if (priv->bufferdma) { + /* In DMA mode, TxFIFO capable of holding 2 packets is enough */ + reqdep *= MIN(2, (1 + addnl)); + } else { + reqdep *= (1 + addnl); + } + if (priv->dynfifosizing) { if (priv->txf_set & ~BIT_MASK(ep_idx)) { dwc2_unset_unused_fifo(dev); @@ -982,17 +1383,28 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, txfaddr = dwc2_get_txfdep(dev, ep_idx - 2) + dwc2_get_txfaddr(dev, ep_idx - 2); } else { - txfaddr = UDC_DWC2_FIFO0_DEPTH + priv->grxfsiz; + txfaddr = priv->rxfifo_depth + + MAX(UDC_DWC2_FIFO0_DEPTH, priv->max_txfifo_depth[0]); + } + + /* Make sure to not set TxFIFO greater than hardware allows */ + txfdep = reqdep; + if (txfdep > priv->max_txfifo_depth[ep_idx]) { + return -ENOMEM; + } + + /* Do not allocate TxFIFO outside the SPRAM */ + if (txfaddr + txfdep > priv->dfifodepth) { + return -ENOMEM; } /* Set FIFO depth (32-bit words) and address */ - txfdep = cfg->mps / 4U; dwc2_set_txf(dev, ep_idx - 1, txfdep, txfaddr); } else { txfdep = dwc2_get_txfdep(dev, ep_idx - 1); txfaddr = dwc2_get_txfaddr(dev, ep_idx - 1); - if (cfg->mps < txfdep * 4U) { + if (reqdep > txfdep) { return -ENOMEM; } @@ -1013,8 +1425,6 @@ static int dwc2_set_dedicated_fifo(const struct device *dev, static int dwc2_ep_control_enable(const struct device *dev, struct udc_ep_config *const cfg) { - struct usb_dwc2_reg *const base = dwc2_get_base(dev); - struct udc_dwc2_data *const priv = udc_get_private(dev); mem_addr_t dxepctl0_reg; uint32_t dxepctl0; @@ -1041,21 +1451,6 @@ static int dwc2_ep_control_enable(const struct device *dev, dxepctl0 |= USB_DWC2_DEPCTL_USBACTEP; - /* - * The following applies to the Control IN endpoint only. - * - * Set endpoint 0 TxFIFO depth when dynfifosizing is enabled. - * Note that only dedicated mode is supported at this time. - */ - if (cfg->addr == USB_CONTROL_EP_IN && priv->dynfifosizing) { - uint32_t gnptxfsiz; - - gnptxfsiz = usb_dwc2_set_gnptxfsiz_nptxfdep(UDC_DWC2_FIFO0_DEPTH) | - usb_dwc2_set_gnptxfsiz_nptxfstaddr(priv->grxfsiz); - - sys_write32(gnptxfsiz, (mem_addr_t)&base->gnptxfsiz); - } - if (cfg->addr == USB_CONTROL_EP_OUT) { int ret; @@ -1101,14 +1496,10 @@ static int udc_dwc2_ep_activate(const struct device *dev, dxepctl_reg = (mem_addr_t)&base->in_ep[ep_idx].diepctl; } - if (cfg->mps > usb_dwc2_get_depctl_mps(UINT16_MAX)) { - return -EINVAL; - } - dxepctl = sys_read32(dxepctl_reg); /* Set max packet size */ dxepctl &= ~USB_DWC2_DEPCTL_MPS_MASK; - dxepctl |= cfg->mps << USB_DWC2_DEPCTL_MPS_POS; + dxepctl |= usb_dwc2_set_depctl_mps(udc_mps_ep_size(cfg)); /* Set endpoint type */ dxepctl &= ~USB_DWC2_DEPCTL_EPTYPE_MASK; @@ -1132,7 +1523,7 @@ static int udc_dwc2_ep_activate(const struct device *dev, return -EINVAL; } - if (USB_EP_DIR_IS_IN(cfg->addr) && cfg->mps != 0U) { + if (USB_EP_DIR_IS_IN(cfg->addr) && udc_mps_ep_size(cfg) != 0U) { int ret = dwc2_set_dedicated_fifo(dev, cfg, &dxepctl); if (ret) { @@ -1281,7 +1672,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, diepint_reg = (mem_addr_t)&base->in_ep[ep_idx].diepint; - dxepctl |= USB_DWC2_DEPCTL_SNAK; + dxepctl |= USB_DWC2_DEPCTL_EPENA | USB_DWC2_DEPCTL_SNAK; if (stall) { /* For IN endpoints STALL is set in addition to SNAK */ dxepctl |= USB_DWC2_DEPCTL_STALL; @@ -1303,7 +1694,7 @@ static void udc_dwc2_ep_disable(const struct device *dev, * were transferred to the host. */ - dwc2_flush_tx_fifo(dev, ep_idx); + dwc2_flush_tx_fifo(dev, usb_dwc2_get_depctl_txfnum(dxepctl)); } udc_ep_set_busy(dev, cfg->addr, false); @@ -1335,7 +1726,8 @@ static int udc_dwc2_ep_deactivate(const struct device *dev, cfg->addr, ep_idx, dxepctl); } - if (USB_EP_DIR_IS_IN(cfg->addr) && cfg->mps != 0U && ep_idx != 0U) { + if (USB_EP_DIR_IS_IN(cfg->addr) && udc_mps_ep_size(cfg) != 0U && + ep_idx != 0U) { dwc2_unset_dedicated_fifo(dev, cfg, &dxepctl); } @@ -1437,13 +1829,16 @@ static int udc_dwc2_set_address(const struct device *dev, const uint8_t addr) { struct usb_dwc2_reg *const base = dwc2_get_base(dev); mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg; + uint32_t dcfg; if (addr > (USB_DWC2_DCFG_DEVADDR_MASK >> USB_DWC2_DCFG_DEVADDR_POS)) { return -EINVAL; } - sys_clear_bits(dcfg_reg, USB_DWC2_DCFG_DEVADDR_MASK); - sys_set_bits(dcfg_reg, usb_dwc2_set_dcfg_devaddr(addr)); + dcfg = sys_read32(dcfg_reg); + dcfg &= ~USB_DWC2_DCFG_DEVADDR_MASK; + dcfg |= usb_dwc2_set_dcfg_devaddr(addr); + sys_write32(dcfg, dcfg_reg); LOG_DBG("Set new address %u for %p", addr, dev); return 0; @@ -1454,14 +1849,14 @@ static int udc_dwc2_test_mode(const struct device *dev, { struct usb_dwc2_reg *const base = dwc2_get_base(dev); mem_addr_t dctl_reg = (mem_addr_t)&base->dctl; - uint32_t tstctl; + uint32_t dctl; if (mode == 0U || mode > USB_DWC2_DCTL_TSTCTL_TESTFE) { return -EINVAL; } - tstctl = usb_dwc2_get_dctl_tstctl(sys_read32(dctl_reg)); - if (tstctl != USB_DWC2_DCTL_TSTCTL_DISABLED) { + dctl = sys_read32(dctl_reg); + if (usb_dwc2_get_dctl_tstctl(dctl) != USB_DWC2_DCTL_TSTCTL_DISABLED) { return -EALREADY; } @@ -1470,7 +1865,8 @@ static int udc_dwc2_test_mode(const struct device *dev, return 0; } - sys_set_bits(dctl_reg, usb_dwc2_set_dctl_tstctl(mode)); + dctl |= usb_dwc2_set_dctl_tstctl(mode); + sys_write32(dctl, dctl_reg); LOG_DBG("Enable Test Mode %u", mode); return 0; @@ -1546,13 +1942,19 @@ static int udc_dwc2_init_controller(const struct device *dev) const struct udc_dwc2_config *const config = dev->config; struct udc_dwc2_data *const priv = udc_get_private(dev); struct usb_dwc2_reg *const base = config->base; + mem_addr_t grxfsiz_reg = (mem_addr_t)&base->grxfsiz; + mem_addr_t gahbcfg_reg = (mem_addr_t)&base->gahbcfg; mem_addr_t gusbcfg_reg = (mem_addr_t)&base->gusbcfg; mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg; + uint32_t dcfg; uint32_t gusbcfg; + uint32_t gahbcfg; uint32_t ghwcfg2; uint32_t ghwcfg3; uint32_t ghwcfg4; + uint32_t val; int ret; + bool hs_phy; ret = dwc2_core_soft_reset(dev); if (ret) { @@ -1577,6 +1979,18 @@ static int udc_dwc2_init_controller(const struct device *dev) sys_write32(gusbcfg, gusbcfg_reg); k_msleep(25); + /* Buffer DMA is always supported in Internal DMA mode. + * TODO: check and support descriptor DMA if available + */ + priv->bufferdma = (usb_dwc2_get_ghwcfg2_otgarch(ghwcfg2) == + USB_DWC2_GHWCFG2_OTGARCH_INTERNALDMA); + + if (!IS_ENABLED(CONFIG_UDC_DWC2_DMA)) { + priv->bufferdma = 0; + } else if (priv->bufferdma) { + LOG_WRN("Experimental DMA enabled"); + } + if (ghwcfg2 & USB_DWC2_GHWCFG2_DYNFIFOSIZING) { LOG_DBG("Dynamic FIFO Sizing is enabled"); priv->dynfifosizing = true; @@ -1616,19 +2030,39 @@ static int udc_dwc2_init_controller(const struct device *dev) LOG_DBG("LPM mode is %s", (ghwcfg3 & USB_DWC2_GHWCFG3_LPMMODE) ? "enabled" : "disabled"); + /* Configure AHB, select Completer or DMA mode */ + gahbcfg = sys_read32(gahbcfg_reg); + + if (priv->bufferdma) { + gahbcfg |= USB_DWC2_GAHBCFG_DMAEN; + } else { + gahbcfg &= ~USB_DWC2_GAHBCFG_DMAEN; + } + + sys_write32(gahbcfg, gahbcfg_reg); + + dcfg = sys_read32(dcfg_reg); + + dcfg &= ~USB_DWC2_DCFG_DESCDMA; + /* Configure PHY and device speed */ + dcfg &= ~USB_DWC2_DCFG_DEVSPD_MASK; switch (usb_dwc2_get_ghwcfg2_hsphytype(ghwcfg2)) { case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUSULPI: __fallthrough; case USB_DWC2_GHWCFG2_HSPHYTYPE_ULPI: gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 | USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_ULPI; - sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBHS20); + dcfg |= USB_DWC2_DCFG_DEVSPD_USBHS20 + << USB_DWC2_DCFG_DEVSPD_POS; + hs_phy = true; break; case USB_DWC2_GHWCFG2_HSPHYTYPE_UTMIPLUS: gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB20 | USB_DWC2_GUSBCFG_ULPI_UTMI_SEL_UTMI; - sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBHS20); + dcfg |= USB_DWC2_DCFG_DEVSPD_USBHS20 + << USB_DWC2_DCFG_DEVSPD_POS; + hs_phy = true; break; case USB_DWC2_GHWCFG2_HSPHYTYPE_NO_HS: __fallthrough; @@ -1638,7 +2072,9 @@ static int udc_dwc2_init_controller(const struct device *dev) gusbcfg |= USB_DWC2_GUSBCFG_PHYSEL_USB11; } - sys_set_bits(dcfg_reg, USB_DWC2_DCFG_DEVSPD_USBFS1148); + dcfg |= USB_DWC2_DCFG_DEVSPD_USBFS1148 + << USB_DWC2_DCFG_DEVSPD_POS; + hs_phy = false; } if (usb_dwc2_get_ghwcfg4_phydatawidth(ghwcfg4)) { @@ -1646,7 +2082,8 @@ static int udc_dwc2_init_controller(const struct device *dev) } /* Update PHY configuration */ - sys_set_bits(gusbcfg_reg, gusbcfg); + sys_write32(gusbcfg, gusbcfg_reg); + sys_write32(dcfg, dcfg_reg); priv->outeps = 0U; for (uint8_t i = 0U; i < priv->numdeveps; i++) { @@ -1663,15 +2100,55 @@ static int udc_dwc2_init_controller(const struct device *dev) LOG_DBG("Number of OUT endpoints %u", priv->outeps); + /* Read and store all TxFIFO depths because Programmed FIFO Depths must + * not exceed the power-on values. + */ + val = sys_read32((mem_addr_t)&base->gnptxfsiz); + priv->max_txfifo_depth[0] = usb_dwc2_get_gnptxfsiz_nptxfdep(val); + for (uint8_t i = 1; i < priv->ineps; i++) { + priv->max_txfifo_depth[i] = dwc2_get_txfdep(dev, i - 1); + } + + priv->rxfifo_depth = usb_dwc2_get_grxfsiz(sys_read32(grxfsiz_reg)); + if (priv->dynfifosizing) { - priv->grxfsiz = UDC_DWC2_GRXFSIZ_DEFAULT + priv->outeps * 2U; - sys_write32(usb_dwc2_set_grxfsiz(priv->grxfsiz), (mem_addr_t)&base->grxfsiz); + uint32_t gnptxfsiz; + uint32_t default_depth; + + /* TODO: For proper runtime FIFO sizing UDC driver would have to + * have prior knowledge of the USB configurations. Only with the + * prior knowledge, the driver will be able to fairly distribute + * available resources. For the time being just use different + * defaults based on maximum configured PHY speed, but this has + * to be revised if e.g. thresholding support would be necessary + * on some target. + */ + if (hs_phy) { + default_depth = UDC_DWC2_GRXFSIZ_HS_DEFAULT; + } else { + default_depth = UDC_DWC2_GRXFSIZ_FS_DEFAULT; + } + default_depth += priv->outeps * 2U; + + /* Driver does not dynamically resize RxFIFO so there is no need + * to store reset value. Read the reset value and make sure that + * the programmed value is not greater than what driver sets. + */ + priv->rxfifo_depth = MIN(priv->rxfifo_depth, default_depth); + sys_write32(usb_dwc2_set_grxfsiz(priv->rxfifo_depth), grxfsiz_reg); + + /* Set TxFIFO 0 depth */ + val = MAX(UDC_DWC2_FIFO0_DEPTH, priv->max_txfifo_depth[0]); + gnptxfsiz = usb_dwc2_set_gnptxfsiz_nptxfdep(val) | + usb_dwc2_set_gnptxfsiz_nptxfstaddr(priv->rxfifo_depth); + + sys_write32(gnptxfsiz, (mem_addr_t)&base->gnptxfsiz); } - LOG_DBG("RX FIFO size %u bytes", priv->grxfsiz * 4); + LOG_DBG("RX FIFO size %u bytes", priv->rxfifo_depth * 4); for (uint8_t i = 1U; i < priv->ineps; i++) { LOG_DBG("TX FIFO%u depth %u addr %u", - i, dwc2_get_txfdep(dev, i), dwc2_get_txfaddr(dev, i)); + i, priv->max_txfifo_depth[i], dwc2_get_txfaddr(dev, i)); } if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, @@ -1690,6 +2167,7 @@ static int udc_dwc2_init_controller(const struct device *dev) sys_write32(USB_DWC2_GINTSTS_OEPINT | USB_DWC2_GINTSTS_IEPINT | USB_DWC2_GINTSTS_ENUMDONE | USB_DWC2_GINTSTS_USBRST | USB_DWC2_GINTSTS_WKUPINT | USB_DWC2_GINTSTS_USBSUSP | + USB_DWC2_GINTSTS_INCOMPISOOUT | USB_DWC2_GINTSTS_INCOMPISOIN | USB_DWC2_GINTSTS_SOF, (mem_addr_t)&base->gintmsk); @@ -1837,6 +2315,7 @@ static int dwc2_driver_preinit(const struct device *dev) config->ep_cfg_out[n].caps.bulk = 1; config->ep_cfg_out[n].caps.interrupt = 1; config->ep_cfg_out[n].caps.iso = 1; + config->ep_cfg_out[n].caps.high_bandwidth = data->caps.hs; config->ep_cfg_out[n].caps.mps = mps; } @@ -1872,6 +2351,7 @@ static int dwc2_driver_preinit(const struct device *dev) config->ep_cfg_in[n].caps.bulk = 1; config->ep_cfg_in[n].caps.interrupt = 1; config->ep_cfg_in[n].caps.iso = 1; + config->ep_cfg_in[n].caps.high_bandwidth = data->caps.hs; config->ep_cfg_in[n].caps.mps = mps; } diff --git a/drivers/usb/udc/udc_it82xx2.c b/drivers/usb/udc/udc_it82xx2.c new file mode 100644 index 00000000000..d14ce36db34 --- /dev/null +++ b/drivers/usb/udc/udc_it82xx2.c @@ -0,0 +1,1477 @@ +/* + * Copyright (c) 2023 ITE Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "udc_common.h" + +#include +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(udc_it82xx2, CONFIG_UDC_DRIVER_LOG_LEVEL); + +#define DT_DRV_COMPAT ite_it82xx2_usb + +#define IT8XXX2_IS_EXTEND_ENDPOINT(n) (USB_EP_GET_IDX(n) >= 4) + +/* Shared FIFO number including FIFO_1/2/3 */ +#define SHARED_FIFO_NUM 3 + +/* The related definitions of the register dc_line_status: 0x51 */ +#define RX_LINE_STATE_MASK (RX_LINE_FULL_SPD | RX_LINE_LOW_SPD) +#define RX_LINE_LOW_SPD 0x02 +#define RX_LINE_FULL_SPD 0x01 +#define RX_LINE_RESET 0x00 + +#define DC_ADDR_NULL 0x00 +#define DC_ADDR_MASK 0x7F + +/* EPN Extend Control 2 Register Mask Definition */ +#define COMPLETED_TRANS 0xF0 + +/* The related definitions of the register EP STATUS: + * 0x41/0x45/0x49/0x4D + */ +#define EP_STATUS_ERROR 0x0F + +/* ENDPOINT[3..0]_CONTROL_REG */ +#define ENDPOINT_EN BIT(0) +#define ENDPOINT_RDY BIT(1) + +/* The bit definitions of the register EP RX/TX FIFO Control: + * EP_RX_FIFO_CONTROL: 0X64/0x84/0xA4/0xC4 + * EP_TX_FIFO_CONTROL: 0X74/0x94/0xB4/0xD4 + */ +#define FIFO_FORCE_EMPTY BIT(0) + +/* The bit definitions of the register Host/Device Control: 0XE0 */ +#define RESET_CORE BIT(1) + +/* ENDPOINT[3..0]_STATUS_REG */ +#define DC_STALL_SENT BIT(5) + +/* DC_INTERRUPT_STATUS_REG */ +#define DC_TRANS_DONE BIT(0) +#define DC_RESET_EVENT BIT(2) +#define DC_SOF_RECEIVED BIT(3) +#define DC_NAK_SENT_INT BIT(4) + +/* DC_CONTROL_REG */ +#define DC_GLOBAL_ENABLE BIT(0) +#define DC_TX_LINE_STATE_DM BIT(1) +#define DC_DIRECT_CONTROL BIT(3) +#define DC_FULL_SPEED_LINE_POLARITY BIT(4) +#define DC_FULL_SPEED_LINE_RATE BIT(5) +#define DC_CONNECT_TO_HOST BIT(6) /* internal pull-up */ + +/* Bit [1:0] represents the TRANSACTION_TYPE as follows: */ +enum it82xx2_transaction_types { + DC_SETUP_TRANS = 0, + DC_IN_TRANS, + DC_OUTDATA_TRANS, + DC_ALL_TRANS +}; + +enum it82xx2_event_type { + IT82xx2_EVT_XFER, + IT82xx2_EVT_SETUP_TOKEN, + IT82xx2_EVT_OUT_TOKEN, + IT82xx2_EVT_IN_TOKEN, +}; + +struct it82xx2_ep_event { + sys_snode_t node; + const struct device *dev; + uint8_t ep; + enum it82xx2_event_type event; +}; + +K_MSGQ_DEFINE(evt_msgq, sizeof(struct it82xx2_ep_event), + CONFIG_UDC_IT82xx2_EVENT_COUNT, sizeof(uint32_t)); + +struct usb_it8xxx2_wuc { + /* WUC control device structure */ + const struct device *dev; + /* WUC pin mask */ + uint8_t mask; +}; + +struct it82xx2_data { + const struct device *dev; + + struct k_fifo fifo; + struct k_work_delayable suspended_work; + + struct k_thread thread_data; + struct k_sem suspended_sem; + + /* FIFO_1/2/3 ready status */ + bool fifo_ready[SHARED_FIFO_NUM]; + + /* FIFO_1/2/3 semaphore */ + struct k_sem fifo_sem[SHARED_FIFO_NUM]; + + /* Record if the previous transaction of endpoint0 is STALL */ + bool stall_is_sent; +}; + +struct usb_it82xx2_config { + struct usb_it82xx2_regs *const base; + const struct pinctrl_dev_config *pcfg; + const struct usb_it8xxx2_wuc wuc; + uint8_t usb_irq; + uint8_t wu_irq; + struct udc_ep_config *ep_cfg_in; + struct udc_ep_config *ep_cfg_out; + void (*make_thread)(const struct device *dev); +}; + +enum it82xx2_ep_ctrl { + EP_IN_DIRECTION_SET, + EP_IOS_ENABLE, + EP_ENABLE, + EP_DATA_SEQ_1, + EP_DATA_SEQ_TOGGLE, + EP_READY_ENABLE, +}; + +static inline void ep_set_halt(const struct device *dev, const uint8_t ep_idx, const bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + union epn0n1_extend_ctrl_reg *ext_ctrl; + uint8_t idx = (ep_idx - 4) / 2; + + ext_ctrl = usb_regs->fifo_regs[EP_EXT_REGS_9X].ext_4_15.epn0n1_ext_ctrl; + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + if (ep_idx % 2) { + ext_ctrl[idx].fields.epn1_send_stall_bit = enable; + } else { + ext_ctrl[idx].fields.epn0_send_stall_bit = enable; + } + } else { + ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit = enable; + } +} + +static volatile void *it82xx2_get_ext_ctrl(const struct device *dev, const uint8_t ep_idx, + const enum it82xx2_ep_ctrl ctrl) +{ + uint8_t idx; + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + union epn0n1_extend_ctrl_reg *epn0n1_ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_9X].ext_4_15.epn0n1_ext_ctrl; + struct epn_ext_ctrl_regs *ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl; + + if (ctrl == EP_IN_DIRECTION_SET || ctrl == EP_ENABLE) { + idx = ((ep_idx - 4) % 3) + 1; + return &ext_ctrl[idx].epn_ext_ctrl1; + } + + idx = (ep_idx - 4) / 2; + return &epn0n1_ext_ctrl[idx]; +} + +static int it82xx2_usb_extend_ep_ctrl(const struct device *dev, const uint8_t ep, + const enum it82xx2_ep_ctrl ctrl, const bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct epn_ext_ctrl_regs *ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl; + volatile union epn_extend_ctrl1_reg *epn_ext_ctrl1 = NULL; + volatile union epn0n1_extend_ctrl_reg *epn0n1_ext_ctrl = NULL; + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (ctrl == EP_IN_DIRECTION_SET || ctrl == EP_ENABLE) { + epn_ext_ctrl1 = it82xx2_get_ext_ctrl(dev, ep_idx, ctrl); + } else { + epn0n1_ext_ctrl = it82xx2_get_ext_ctrl(dev, ep_idx, ctrl); + } + + switch (ctrl) { + case EP_IOS_ENABLE: + if (ep_idx % 2) { + epn0n1_ext_ctrl->fields.epn1_iso_enable_bit = enable; + } else { + epn0n1_ext_ctrl->fields.epn0_iso_enable_bit = enable; + } + break; + case EP_DATA_SEQ_1: + if (ep_idx % 2) { + epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit = enable; + } else { + epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit = enable; + } + break; + case EP_DATA_SEQ_TOGGLE: + if (!enable) { + break; + } + if (ep_idx % 2) { + if (epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit) { + epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit = 0; + } else { + epn0n1_ext_ctrl->fields.epn1_outdata_sequence_bit = 1; + } + } else { + if (epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit) { + epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit = 0; + } else { + epn0n1_ext_ctrl->fields.epn0_outdata_sequence_bit = 1; + } + } + break; + case EP_IN_DIRECTION_SET: + if (((ep_idx - 4) / 3 == 0)) { + epn_ext_ctrl1->fields.epn0_direction_bit = enable; + } else if (((ep_idx - 4) / 3 == 1)) { + epn_ext_ctrl1->fields.epn3_direction_bit = enable; + } else if (((ep_idx - 4) / 3 == 2)) { + epn_ext_ctrl1->fields.epn6_direction_bit = enable; + } else if (((ep_idx - 4) / 3 == 3)) { + epn_ext_ctrl1->fields.epn9_direction_bit = enable; + } else { + LOG_ERR("Invalid endpoint 0x%x for control type 0x%x", ep, ctrl); + return -EINVAL; + } + break; + case EP_ENABLE: + if (((ep_idx - 4) / 3 == 0)) { + epn_ext_ctrl1->fields.epn0_enable_bit = enable; + } else if (((ep_idx - 4) / 3 == 1)) { + epn_ext_ctrl1->fields.epn3_enable_bit = enable; + } else if (((ep_idx - 4) / 3 == 2)) { + epn_ext_ctrl1->fields.epn6_enable_bit = enable; + } else if (((ep_idx - 4) / 3 == 3)) { + epn_ext_ctrl1->fields.epn9_enable_bit = enable; + } else { + LOG_ERR("Invalid endpoint 0x%x for control type 0x%x", ep, ctrl); + return -EINVAL; + } + break; + case EP_READY_ENABLE: + int idx = ((ep_idx - 4) % 3) + 1; + + (enable) ? (ext_ctrl[idx].epn_ext_ctrl2 |= BIT((ep_idx - 4) / 3)) + : (ext_ctrl[idx].epn_ext_ctrl2 &= ~BIT((ep_idx - 4) / 3)); + break; + default: + LOG_ERR("Unknown control type 0x%x", ctrl); + return -EINVAL; + } + + return 0; +} + +static int it82xx2_usb_ep_ctrl(const struct device *dev, uint8_t ep, enum it82xx2_ep_ctrl ctrl, + bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + return -EINVAL; + } + + switch (ctrl) { + case EP_IN_DIRECTION_SET: + ep_regs[ep_idx].ep_ctrl.fields.direction_bit = enable; + break; + case EP_IOS_ENABLE: + ep_regs[ep_idx].ep_ctrl.fields.iso_enable_bit = enable; + break; + case EP_ENABLE: + ep_regs[ep_idx].ep_ctrl.fields.enable_bit = enable; + break; + case EP_READY_ENABLE: + ep_regs[ep_idx].ep_ctrl.fields.ready_bit = enable; + break; + case EP_DATA_SEQ_1: + ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = enable; + break; + case EP_DATA_SEQ_TOGGLE: + if (!enable) { + break; + } + if (ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit) { + ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 0; + } else { + ep_regs[ep_idx].ep_ctrl.fields.outdata_sequence_bit = 1; + } + break; + default: + LOG_ERR("Unknown control type 0x%x", ctrl); + return -EINVAL; + } + return 0; +} + +static int it82xx2_usb_set_ep_ctrl(const struct device *dev, uint8_t ep, enum it82xx2_ep_ctrl ctrl, + bool enable) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + int ret = 0; + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + ret = it82xx2_usb_extend_ep_ctrl(dev, ep, ctrl, enable); + } else { + ret = it82xx2_usb_ep_ctrl(dev, ep, ctrl, enable); + } + return ret; +} + +/* Standby(deep doze) mode enable/disable */ +static void it82xx2_enable_standby_state(bool enable) +{ + if (enable) { + pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + } else { + pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + } +} + +/* Wake-up interrupt (USB D+) Enable/Disable */ +static void it82xx2_enable_wu_irq(const struct device *dev, bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + + /* Clear pending interrupt */ + it8xxx2_wuc_clear_status(config->wuc.dev, config->wuc.mask); + + if (enable) { + irq_enable(config->wu_irq); + } else { + irq_disable(config->wu_irq); + } +} + +static void it82xx2_wu_isr(const void *arg) +{ + const struct device *dev = arg; + + it82xx2_enable_wu_irq(dev, false); + it82xx2_enable_standby_state(false); + LOG_DBG("USB D+ (WU) Triggered"); +} + +static void it8xxx2_usb_dc_wuc_init(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + + /* Initializing the WUI */ + it8xxx2_wuc_set_polarity(config->wuc.dev, config->wuc.mask, WUC_TYPE_EDGE_FALLING); + it8xxx2_wuc_clear_status(config->wuc.dev, config->wuc.mask); + + /* Enabling the WUI */ + it8xxx2_wuc_enable(config->wuc.dev, config->wuc.mask); + + /* Connect WU (USB D+) interrupt but make it disabled initially */ + irq_connect_dynamic(config->wu_irq, 0, it82xx2_wu_isr, dev, 0); +} + +/* The ep_fifo_res[ep_idx % SHARED_FIFO_NUM] where the SHARED_FIFO_NUM is 3 represents the + * EP mapping because when (ep_idx % SHARED_FIFO_NUM) is 3, it actually means the EP0. + */ +static const uint8_t ep_fifo_res[SHARED_FIFO_NUM] = {3, 1, 2}; + +static int it82xx2_usb_fifo_ctrl(const struct device *dev, uint8_t ep) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + volatile uint8_t *ep_fifo_ctrl = usb_regs->fifo_regs[EP_EXT_REGS_BX].fifo_ctrl.ep_fifo_ctrl; + uint8_t fifon_ctrl = (ep_fifo_res[ep_idx % SHARED_FIFO_NUM] - 1) * 2; + int ret = 0; + + if (ep_idx == 0) { + LOG_ERR("Invalid endpoint 0x%x", ep); + return -EINVAL; + } + + if (USB_EP_DIR_IS_IN(ep)) { + if (ep_idx < 8) { + ep_fifo_ctrl[fifon_ctrl] = BIT(ep_idx); + ep_fifo_ctrl[fifon_ctrl + 1] = 0x0; + } else { + ep_fifo_ctrl[fifon_ctrl] = 0x0; + ep_fifo_ctrl[fifon_ctrl + 1] = BIT(ep_idx - 8); + } + } else if (USB_EP_DIR_IS_OUT(ep)) { + if (ep_idx < 8) { + ep_fifo_ctrl[fifon_ctrl] |= BIT(ep_idx); + } else { + ep_fifo_ctrl[fifon_ctrl + 1] |= BIT(ep_idx - 8); + } + } else { + LOG_ERR("Failed to set fifo control register for ep 0x%x", ep); + ret = -EINVAL; + } + + return ret; +} + +static void it82xx2_event_submit(const struct device *dev, const uint8_t ep, + const enum it82xx2_event_type event) +{ + struct it82xx2_ep_event evt; + + evt.dev = dev; + evt.ep = ep; + evt.event = event; + k_msgq_put(&evt_msgq, &evt, K_NO_WAIT); +} + +static int it82xx2_ep_enqueue(const struct device *dev, struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + udc_buf_put(cfg, buf); + + it82xx2_event_submit(dev, cfg->addr, IT82xx2_EVT_XFER); + return 0; +} + +static int it82xx2_ep_dequeue(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct net_buf *buf; + unsigned int lock_key; + uint8_t fifo_idx; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + lock_key = irq_lock(); + if (USB_EP_DIR_IS_IN(cfg->addr)) { + ff_regs[fifo_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + } else { + ff_regs[fifo_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY; + } + irq_unlock(lock_key); + + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + udc_ep_set_busy(dev, cfg->addr, false); + + return 0; +} + +static inline void ctrl_ep_stall_workaround(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE; + struct it82xx2_data *priv = udc_get_private(dev); + unsigned int lock_key; + uint32_t idx = 0; + + priv->stall_is_sent = true; + lock_key = irq_lock(); + ep_set_halt(dev, 0, true); + it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); + + /* It82xx2 does not support clearing the STALL bit by hardware; instead, the STALL bit need + * to be cleared by firmware. The SETUP token will be STALLed, which isn't compliant to + * USB specification, if firmware clears the STALL bit too late. Due to this hardware + * limitations, device controller polls to check if the stall bit has been transmitted for + * 3ms and then disables it after responsing STALLed. + */ + while (idx < 198 && !(ep_regs[0].ep_status & DC_STALL_SENT)) { + /* wait 15.15us */ + gctrl_regs->GCTRL_WNCKR = 0; + idx++; + } + + if (idx < 198) { + ep_set_halt(dev, 0, false); + } + irq_unlock(lock_key); +} + +static int it82xx2_ep_set_halt(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + if (ep_idx == 0) { + ctrl_ep_stall_workaround(dev); + } else { + ep_set_halt(dev, ep_idx, true); + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_READY_ENABLE, true); + } + + LOG_DBG("Endpoint 0x%x is halted", cfg->addr); + + return 0; +} + +static int it82xx2_ep_clear_halt(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + ep_set_halt(dev, ep_idx, false); + + LOG_DBG("Endpoint 0x%x clear halted", cfg->addr); + + return 0; +} + +static int it82xx2_ep_enable(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + /* Configure endpoint */ + if (ep_idx != 0) { + if (USB_EP_DIR_IS_IN(cfg->addr)) { + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IN_DIRECTION_SET, true); + } else { + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IN_DIRECTION_SET, false); + it82xx2_usb_fifo_ctrl(dev, cfg->addr); + } + + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_BULK: + __fallthrough; + case USB_EP_TYPE_INTERRUPT: + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IOS_ENABLE, false); + break; + case USB_EP_TYPE_ISO: + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_IOS_ENABLE, true); + break; + case USB_EP_TYPE_CONTROL: + __fallthrough; + default: + return -ENOTSUP; + } + } + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + uint8_t fifo_idx; + + fifo_idx = ep_fifo_res[ep_idx % SHARED_FIFO_NUM]; + it82xx2_usb_set_ep_ctrl(dev, fifo_idx, EP_ENABLE, true); + } + + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_ENABLE, true); + + LOG_DBG("Endpoint 0x%02x is enabled", cfg->addr); + + return 0; +} + +static int it82xx2_ep_disable(const struct device *dev, struct udc_ep_config *const cfg) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(cfg->addr); + + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_ENABLE, false); + + LOG_DBG("Endpoint 0x%02x is disabled", cfg->addr); + + return 0; +} + +static int it82xx2_host_wakeup(const struct device *dev) +{ + struct it82xx2_data *priv = udc_get_private(dev); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + int ret; + + if (udc_is_suspended(dev)) { + usb_regs->dc_control = DC_GLOBAL_ENABLE | DC_FULL_SPEED_LINE_POLARITY | + DC_FULL_SPEED_LINE_RATE | DC_DIRECT_CONTROL | + DC_TX_LINE_STATE_DM | DC_CONNECT_TO_HOST; + + /* The remote wakeup device must hold the resume signal for */ + /* at least 1 ms but for no more than 15 ms */ + k_msleep(2); + + usb_regs->dc_control = DC_GLOBAL_ENABLE | DC_FULL_SPEED_LINE_POLARITY | + DC_FULL_SPEED_LINE_RATE | DC_CONNECT_TO_HOST; + + ret = k_sem_take(&priv->suspended_sem, K_MSEC(500)); + if (ret < 0) { + LOG_ERR("Failed to wake up host"); + } + } + + return 0; +} + +static int it82xx2_set_address(const struct device *dev, const uint8_t addr) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + usb_regs->dc_address = addr & DC_ADDR_MASK; + + LOG_DBG("Set usb address 0x%02x", addr); + + return 0; +} + +static int it82xx2_usb_dc_ip_init(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + /* reset usb controller */ + usb_regs->host_device_control = RESET_CORE; + k_msleep(1); + usb_regs->port0_misc_control &= ~(PULL_DOWN_EN); + usb_regs->port1_misc_control &= ~(PULL_DOWN_EN); + + /* clear reset bit */ + usb_regs->host_device_control = 0; + + usb_regs->dc_interrupt_status = DC_TRANS_DONE | DC_RESET_EVENT | DC_SOF_RECEIVED; + + usb_regs->dc_interrupt_mask = 0x00; + usb_regs->dc_interrupt_mask = DC_TRANS_DONE | DC_RESET_EVENT | DC_SOF_RECEIVED; + + usb_regs->dc_address = DC_ADDR_NULL; + + return 0; +} + +static void it82xx2_enable_sof_int(const struct device *dev, bool enable) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + usb_regs->dc_interrupt_status = DC_SOF_RECEIVED; + if (enable) { + usb_regs->dc_interrupt_mask |= DC_SOF_RECEIVED; + } else { + usb_regs->dc_interrupt_mask &= ~DC_SOF_RECEIVED; + } +} + +void it82xx2_dc_reset(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct it82xx2_data *priv = udc_get_private(dev); + + for (uint8_t ep_idx = 0; ep_idx < 4; ep_idx++) { + ff_regs[ep_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY; + ff_regs[ep_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + } + + ep_regs[0].ep_ctrl.value = ENDPOINT_EN; + usb_regs->dc_address = DC_ADDR_NULL; + usb_regs->dc_interrupt_status = DC_NAK_SENT_INT | DC_SOF_RECEIVED; + + priv->fifo_ready[0] = false; + priv->fifo_ready[1] = false; + priv->fifo_ready[2] = false; + k_sem_give(&priv->fifo_sem[0]); + k_sem_give(&priv->fifo_sem[1]); + k_sem_give(&priv->fifo_sem[2]); +} + +static int it82xx2_xfer_in_data(const struct device *dev, uint8_t ep, struct net_buf *buf) +{ + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct it82xx2_data *priv = udc_get_private(dev); + struct udc_ep_config *ep_cfg = udc_get_ep_cfg(dev, ep); + uint8_t fifo_idx; + size_t len; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + if (ep_idx == 0) { + ff_regs[ep_idx].ep_tx_fifo_ctrl = FIFO_FORCE_EMPTY; + } else { + k_sem_take(&priv->fifo_sem[fifo_idx - 1], K_FOREVER); + it82xx2_usb_fifo_ctrl(dev, ep); + } + + len = MIN(buf->len, udc_mps_ep_size(ep_cfg)); + + for (size_t i = 0; i < len; i++) { + ff_regs[fifo_idx].ep_tx_fifo_data = buf->data[i]; + } + + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + it82xx2_usb_extend_ep_ctrl(dev, ep_idx, EP_READY_ENABLE, true); + } + it82xx2_usb_set_ep_ctrl(dev, fifo_idx, EP_READY_ENABLE, true); + + if (ep_idx != 0) { + priv->fifo_ready[fifo_idx - 1] = true; + } + + LOG_DBG("Writed %d packets to endpoint%d tx fifo", buf->len, ep_idx); + + return 0; +} + +static int it82xx2_xfer_out_data(const struct device *dev, uint8_t ep, struct net_buf *buf) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t fifo_idx; + size_t len; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + if (ep_regs[fifo_idx].ep_status & EP_STATUS_ERROR) { + LOG_WRN("endpoint%d error status 0x%02x", ep_idx, ep_regs[fifo_idx].ep_status); + return -EINVAL; + } + + len = (uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_lsb + + (((uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_msb) << 8); + + len = MIN(net_buf_tailroom(buf), len); + uint8_t *data_ptr = net_buf_tail(buf); + + for (size_t idx = 0; idx < len; idx++) { + data_ptr[idx] = ff_regs[fifo_idx].ep_rx_fifo_data; + } + + net_buf_add(buf, len); + + return 0; +} + +static int work_handler_xfer_continue(const struct device *dev, uint8_t ep, struct net_buf *buf) +{ + int ret = 0; + + if (USB_EP_DIR_IS_OUT(ep)) { + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + struct it82xx2_data *priv = udc_get_private(dev); + uint8_t fifo_idx; + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + it82xx2_usb_set_ep_ctrl(dev, ep_idx, EP_READY_ENABLE, true); + if (IT8XXX2_IS_EXTEND_ENDPOINT(ep_idx)) { + it82xx2_usb_set_ep_ctrl(dev, fifo_idx, EP_READY_ENABLE, true); + } + if (ep_idx != 0) { + priv->fifo_ready[fifo_idx - 1] = true; + } + } else { + ret = it82xx2_xfer_in_data(dev, ep, buf); + } + + return ret; +} + +static int work_handler_xfer_next(const struct device *dev, uint8_t ep) +{ + struct net_buf *buf; + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + + return work_handler_xfer_continue(dev, ep, buf); +} + +/* + * Allocate buffer and initiate a new control OUT transfer, + * use successive buffer descriptor when next is true. + */ +static int it82xx2_ctrl_feed_dout(const struct device *dev, const size_t length) +{ + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + struct net_buf *buf; + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); + if (buf == NULL) { + return -ENOMEM; + } + udc_buf_put(cfg, buf); + + it82xx2_usb_set_ep_ctrl(dev, 0, EP_READY_ENABLE, true); + + return 0; +} + +static bool it82xx2_fake_token(const struct device *dev, uint8_t ep, uint8_t token_type) +{ + struct it82xx2_data *priv = udc_get_private(dev); + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + uint8_t fifo_idx; + bool is_fake = true; + + if (ep_idx == 0) { + switch (token_type) { + case DC_IN_TRANS: + if (priv->stall_is_sent) { + return true; + } + is_fake = !udc_ctrl_stage_is_data_in(dev) && + !udc_ctrl_stage_is_status_in(dev) && + !udc_ctrl_stage_is_no_data(dev); + break; + case DC_OUTDATA_TRANS: + is_fake = !udc_ctrl_stage_is_data_out(dev) && + !udc_ctrl_stage_is_status_out(dev); + break; + default: + LOG_ERR("Invalid token type"); + break; + } + } else { + fifo_idx = ep_fifo_res[ep_idx % SHARED_FIFO_NUM]; + + if (!priv->fifo_ready[fifo_idx - 1]) { + is_fake = true; + } else { + priv->fifo_ready[fifo_idx - 1] = false; + is_fake = false; + } + } + + return is_fake; +} + +static inline int work_handler_in(const struct device *dev, uint8_t ep) +{ + struct it82xx2_data *priv = udc_get_private(dev); + struct udc_ep_config *ep_cfg; + struct net_buf *buf; + uint8_t fifo_idx; + int err = 0; + + if (it82xx2_fake_token(dev, ep, DC_IN_TRANS)) { + return 0; + } + + if (ep != USB_CONTROL_EP_IN) { + fifo_idx = ep_fifo_res[USB_EP_GET_IDX(ep) % SHARED_FIFO_NUM]; + k_sem_give(&priv->fifo_sem[fifo_idx - 1]); + } + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + ep_cfg = udc_get_ep_cfg(dev, ep); + + net_buf_pull(buf, MIN(buf->len, udc_mps_ep_size(ep_cfg))); + + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_TOGGLE, true); + + if (buf->len) { + work_handler_xfer_continue(dev, ep, buf); + return 0; + } + + if (udc_ep_buf_has_zlp(buf)) { + work_handler_xfer_continue(dev, ep, buf); + udc_ep_buf_clear_zlp(buf); + return 0; + } + + buf = udc_buf_get(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + + udc_ep_set_busy(dev, ep, false); + + if (ep == USB_CONTROL_EP_IN) { + if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { + /* Status stage finished, notify upper layer */ + udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_out(dev)) { + /* + * IN transfer finished, release buffer, + * Feed control OUT buffer for status stage. + */ + net_buf_unref(buf); + return it82xx2_ctrl_feed_dout(dev, 0U); + } + return err; + } + + return udc_submit_ep_event(dev, buf, 0); +} + +static inline int work_handler_setup(const struct device *dev, uint8_t ep) +{ + struct it82xx2_data *priv = udc_get_private(dev); + struct net_buf *buf; + int err = 0; + + if (udc_ctrl_stage_is_status_out(dev)) { + /* out -> setup */ + buf = udc_buf_get(dev, USB_CONTROL_EP_OUT); + if (buf) { + udc_ep_set_busy(dev, USB_CONTROL_EP_OUT, false); + net_buf_unref(buf); + } + } + + if (udc_ctrl_stage_is_status_in(dev) || udc_ctrl_stage_is_no_data(dev)) { + /* in -> setup */ + work_handler_in(dev, USB_CONTROL_EP_IN); + } + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, sizeof(struct usb_setup_packet)); + if (buf == NULL) { + LOG_ERR("Failed to allocate buffer"); + return -ENOMEM; + } + + udc_ep_buf_set_setup(buf); + it82xx2_xfer_out_data(dev, ep, buf); + if (buf->len != sizeof(struct usb_setup_packet)) { + LOG_DBG("buffer length %d read from chip", buf->len); + net_buf_unref(buf); + return 0; + } + + priv->stall_is_sent = false; + LOG_HEXDUMP_DBG(buf->data, buf->len, "setup:"); + + udc_ctrl_update_stage(dev, buf); + + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); + + if (udc_ctrl_stage_is_data_out(dev)) { + /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", buf); + err = it82xx2_ctrl_feed_dout(dev, udc_data_stage_length(buf)); + if (err == -ENOMEM) { + err = udc_submit_ep_event(dev, buf, err); + } + } else if (udc_ctrl_stage_is_data_in(dev)) { + udc_ctrl_submit_s_in_status(dev); + } else { + udc_ctrl_submit_s_status(dev); + } + + return err; +} + +static inline int work_handler_out(const struct device *dev, uint8_t ep) +{ + struct net_buf *buf; + int err = 0; + + const uint8_t ep_idx = USB_EP_GET_IDX(ep); + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + struct udc_ep_config *ep_cfg; + uint8_t fifo_idx; + size_t len; + + if (it82xx2_fake_token(dev, ep, DC_OUTDATA_TRANS)) { + return 0; + } + + buf = udc_buf_get(dev, ep); + if (buf == NULL) { + return -ENODATA; + } + + udc_ep_set_busy(dev, ep, false); + + fifo_idx = ep_idx > 0 ? ep_fifo_res[ep_idx % SHARED_FIFO_NUM] : 0; + len = (uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_lsb + + (((uint16_t)ff_regs[fifo_idx].ep_rx_fifo_dcnt_msb) << 8); + + if (ep == USB_CONTROL_EP_OUT) { + if (udc_ctrl_stage_is_status_out(dev) && len != 0) { + LOG_DBG("Handle early setup token"); + /* Notify upper layer */ + udc_ctrl_submit_status(dev, buf); + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + return 0; + } + } + + ep_cfg = udc_get_ep_cfg(dev, ep); + if (len > udc_mps_ep_size(ep_cfg)) { + LOG_ERR("Failed to handle this packet due to the packet size"); + return -ENOBUFS; + } + + it82xx2_xfer_out_data(dev, ep, buf); + + if (ep == USB_CONTROL_EP_OUT) { + if (udc_ctrl_stage_is_status_out(dev)) { + /* Status stage finished, notify upper layer */ + udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_in(dev)) { + it82xx2_usb_set_ep_ctrl(dev, ep, EP_DATA_SEQ_1, true); + err = udc_ctrl_submit_s_out_status(dev, buf); + } + } else { + err = udc_submit_ep_event(dev, buf, 0); + } + + return err; +} + +static void xfer_work_handler(const struct device *dev) +{ + while (true) { + struct it82xx2_ep_event evt; + int err = 0; + + k_msgq_get(&evt_msgq, &evt, K_FOREVER); + + switch (evt.event) { + case IT82xx2_EVT_SETUP_TOKEN: + err = work_handler_setup(evt.dev, evt.ep); + break; + case IT82xx2_EVT_IN_TOKEN: + err = work_handler_in(evt.dev, evt.ep); + break; + case IT82xx2_EVT_OUT_TOKEN: + err = work_handler_out(evt.dev, evt.ep); + break; + case IT82xx2_EVT_XFER: + break; + default: + LOG_ERR("Unknown event type 0x%x", evt.event); + err = -EINVAL; + break; + } + + if (err) { + udc_submit_event(evt.dev, UDC_EVT_ERROR, err); + } + + if (evt.ep != USB_CONTROL_EP_OUT && !udc_ep_is_busy(evt.dev, evt.ep)) { + if (work_handler_xfer_next(evt.dev, evt.ep) == 0) { + udc_ep_set_busy(evt.dev, evt.ep, true); + } + } + } +} + +static inline bool it82xx2_check_ep0_stall(const struct device *dev, const uint8_t ep_idx, + const uint8_t transtype) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct it82xx2_usb_ep_fifo_regs *ff_regs = usb_regs->fifo_regs; + + if (ep_idx != 0) { + return false; + } + + /* Check if the stall bit is set */ + if (ep_regs[ep_idx].ep_ctrl.fields.send_stall_bit) { + ep_set_halt(dev, ep_idx, false); + if (transtype == DC_SETUP_TRANS) { + ff_regs[ep_idx].ep_rx_fifo_ctrl = FIFO_FORCE_EMPTY; + } + LOG_ERR("Cleared stall bit"); + return true; + } + + /* Check if the IN transaction is STALL */ + if ((transtype == DC_IN_TRANS) && (ep_regs[ep_idx].ep_status & DC_STALL_SENT)) { + return true; + } + + return false; +} + +static void it82xx2_usb_xfer_done(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_usb_ep_regs *ep_regs = usb_regs->usb_ep_regs; + struct epn_ext_ctrl_regs *epn_ext_ctrl = + usb_regs->fifo_regs[EP_EXT_REGS_DX].ext_0_3.epn_ext_ctrl; + + for (uint8_t fifo_idx = 0; fifo_idx < 4; fifo_idx++) { + uint8_t ep, ep_idx, ep_ctrl, transtype; + + ep_ctrl = ep_regs[fifo_idx].ep_ctrl.value; + transtype = ep_regs[fifo_idx].ep_transtype_sts & DC_ALL_TRANS; + + if (!(ep_ctrl & ENDPOINT_EN) || (ep_ctrl & ENDPOINT_RDY)) { + continue; + } + + if (fifo_idx == 0) { + ep_idx = 0; + if (it82xx2_check_ep0_stall(dev, ep_idx, transtype)) { + continue; + } + } else { + ep_idx = (epn_ext_ctrl[fifo_idx].epn_ext_ctrl2 & COMPLETED_TRANS) >> 4; + if (ep_idx == 0) { + continue; + } + } + + switch (transtype) { + case DC_SETUP_TRANS: + /* SETUP transaction done */ + if (ep_idx != 0) { + break; + } + it82xx2_event_submit(dev, ep_idx, IT82xx2_EVT_SETUP_TOKEN); + break; + case DC_IN_TRANS: + /* IN transaction done */ + ep = USB_EP_DIR_IN | ep_idx; + it82xx2_event_submit(dev, ep, IT82xx2_EVT_IN_TOKEN); + break; + case DC_OUTDATA_TRANS: + /* OUT transaction done */ + ep = USB_EP_DIR_OUT | ep_idx; + it82xx2_event_submit(dev, ep, IT82xx2_EVT_OUT_TOKEN); + break; + default: + LOG_ERR("Unknown transaction type"); + break; + } + } +} + +static void it82xx2_usb_dc_isr(const void *arg) +{ + const struct device *dev = arg; + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_data *priv = udc_get_private(dev); + + uint8_t status = usb_regs->dc_interrupt_status & + usb_regs->dc_interrupt_mask; /* mask non enable int */ + + /* reset event */ + if (status & DC_RESET_EVENT) { + if ((usb_regs->dc_line_status & RX_LINE_STATE_MASK) == RX_LINE_RESET) { + it82xx2_dc_reset(dev); + usb_regs->dc_interrupt_status = DC_RESET_EVENT; + + udc_submit_event(dev, UDC_EVT_RESET, 0); + return; + } + usb_regs->dc_interrupt_status = DC_RESET_EVENT; + } + + /* sof received */ + if (status & DC_SOF_RECEIVED) { + it82xx2_enable_sof_int(dev, false); + k_work_reschedule(&priv->suspended_work, K_MSEC(5)); + } + + /* transaction done */ + if (status & DC_TRANS_DONE) { + /* clear interrupt before new transaction */ + usb_regs->dc_interrupt_status = DC_TRANS_DONE; + if (udc_is_suspended(dev) && udc_is_enabled(dev)) { + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + k_sem_give(&priv->suspended_sem); + } + it82xx2_usb_xfer_done(dev); + return; + } +} + +static void suspended_handler(struct k_work *item) +{ + struct k_work_delayable *dwork = k_work_delayable_from_work(item); + struct it82xx2_data *priv = CONTAINER_OF(dwork, struct it82xx2_data, suspended_work); + const struct device *dev = priv->dev; + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + if (usb_regs->dc_interrupt_status & DC_SOF_RECEIVED) { + usb_regs->dc_interrupt_status = DC_SOF_RECEIVED; + if (udc_is_suspended(dev) && udc_is_enabled(dev)) { + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + k_sem_give(&priv->suspended_sem); + } + k_work_reschedule(&priv->suspended_work, K_MSEC(5)); + return; + } + + it82xx2_enable_sof_int(dev, true); + + if (!udc_is_suspended(dev) && udc_is_enabled(dev)) { + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + it82xx2_enable_wu_irq(dev, true); + it82xx2_enable_standby_state(true); + + k_sem_reset(&priv->suspended_sem); + } +} + +static int it82xx2_enable(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + struct it82xx2_data *priv = udc_get_private(dev); + + k_sem_init(&priv->suspended_sem, 0, 1); + k_work_init_delayable(&priv->suspended_work, suspended_handler); + + /* Initialize FIFO ready status */ + priv->fifo_ready[0] = false; + priv->fifo_ready[1] = false; + priv->fifo_ready[2] = false; + + /* Initialize FIFO semaphore */ + k_sem_init(&priv->fifo_sem[0], 1, 1); + k_sem_init(&priv->fifo_sem[1], 1, 1); + k_sem_init(&priv->fifo_sem[2], 1, 1); + + usb_regs->dc_control = DC_GLOBAL_ENABLE | DC_FULL_SPEED_LINE_POLARITY | + DC_FULL_SPEED_LINE_RATE | DC_CONNECT_TO_HOST; + + /* Enable USB D+ and USB interrupts */ + it82xx2_enable_wu_irq(dev, true); + irq_enable(config->usb_irq); + + return 0; +} + +static int it82xx2_disable(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct usb_it82xx2_regs *const usb_regs = config->base; + + irq_disable(config->usb_irq); + + /* stop pull-up D+ D-*/ + usb_regs->dc_control &= ~DC_CONNECT_TO_HOST; + + return 0; +} + +static int it82xx2_init(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct gctrl_it8xxx2_regs *const gctrl_regs = GCTRL_IT8XXX2_REGS_BASE; + int ret; + + /* + * Disable USB debug path , prevent CPU enter + * JTAG mode and then reset by USB command. + */ + gctrl_regs->GCTRL_MCCR &= ~(IT8XXX2_GCTRL_MCCR_USB_EN); + gctrl_regs->gctrl_pmer2 |= IT8XXX2_GCTRL_PMER2_USB_PAD_EN; + + it82xx2_usb_dc_ip_init(dev); + + ret = udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, USB_EP_TYPE_CONTROL, + config->ep_cfg_out[0].caps.mps, 0); + if (ret) { + LOG_ERR("Failed to enable ep 0x%02x", USB_CONTROL_EP_OUT); + return ret; + } + + ret = udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, USB_EP_TYPE_CONTROL, + config->ep_cfg_in[0].caps.mps, 0); + if (ret) { + LOG_ERR("Failed to enable ep 0x%02x", USB_CONTROL_EP_IN); + return ret; + } + return 0; +} + +static int it82xx2_shutdown(const struct device *dev) +{ + if (udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT)) { + LOG_ERR("Failed to disable control endpoint"); + return -EIO; + } + + if (udc_ep_disable_internal(dev, USB_CONTROL_EP_IN)) { + LOG_ERR("Failed to disable control endpoint"); + return -EIO; + } + + return 0; +} + +static int it82xx2_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int it82xx2_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + +static const struct udc_api it82xx2_api = { + .ep_enqueue = it82xx2_ep_enqueue, + .ep_dequeue = it82xx2_ep_dequeue, + .ep_set_halt = it82xx2_ep_set_halt, + .ep_clear_halt = it82xx2_ep_clear_halt, + .ep_try_config = NULL, + .ep_enable = it82xx2_ep_enable, + .ep_disable = it82xx2_ep_disable, + .host_wakeup = it82xx2_host_wakeup, + .set_address = it82xx2_set_address, + .enable = it82xx2_enable, + .disable = it82xx2_disable, + .init = it82xx2_init, + .shutdown = it82xx2_shutdown, + .lock = it82xx2_lock, + .unlock = it82xx2_unlock, +}; + +static int it82xx2_usb_driver_preinit(const struct device *dev) +{ + const struct usb_it82xx2_config *config = dev->config; + struct udc_data *data = dev->data; + struct it82xx2_data *priv = udc_get_private(dev); + int err; + + k_mutex_init(&data->mutex); + k_fifo_init(&priv->fifo); + + err = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (err < 0) { + LOG_ERR("Failed to configure usb pins"); + return err; + } + + for (int i = 0; i < MAX_NUM_ENDPOINTS; i++) { + config->ep_cfg_out[i].caps.out = 1; + if (i == 0) { + config->ep_cfg_out[i].caps.control = 1; + config->ep_cfg_out[i].caps.mps = USB_CONTROL_EP_MPS; + } else if ((i % 3) == 2) { + config->ep_cfg_out[i].caps.bulk = 1; + config->ep_cfg_out[i].caps.interrupt = 1; + config->ep_cfg_out[i].caps.iso = 1; + config->ep_cfg_out[i].caps.mps = 64; + } + + config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; + err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + for (int i = 0; i < MAX_NUM_ENDPOINTS; i++) { + config->ep_cfg_in[i].caps.in = 1; + if (i == 0) { + config->ep_cfg_in[i].caps.control = 1; + config->ep_cfg_in[i].caps.mps = USB_CONTROL_EP_MPS; + } else if ((i % 3) != 2) { + config->ep_cfg_in[i].caps.bulk = 1; + config->ep_cfg_in[i].caps.interrupt = 1; + config->ep_cfg_in[i].caps.iso = 1; + config->ep_cfg_in[i].caps.mps = 64; + } + + config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; + err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + data->caps.rwup = true; + data->caps.mps0 = UDC_MPS0_64; + + priv->dev = dev; + + config->make_thread(dev); + + /* Initializing WU (USB D+) */ + it8xxx2_usb_dc_wuc_init(dev); + + /* Connect USB interrupt */ + irq_connect_dynamic(config->usb_irq, 0, it82xx2_usb_dc_isr, dev, 0); + + return 0; +} + +#define IT82xx2_USB_DEVICE_DEFINE(n) \ + K_KERNEL_STACK_DEFINE(udc_it82xx2_stack_##n, CONFIG_UDC_IT82xx2_STACK_SIZE); \ + \ + static void udc_it82xx2_thread_##n(void *dev, void *arg1, void *arg2) \ + { \ + ARG_UNUSED(arg1); \ + ARG_UNUSED(arg2); \ + xfer_work_handler(dev); \ + } \ + \ + static void udc_it82xx2_make_thread_##n(const struct device *dev) \ + { \ + struct it82xx2_data *priv = udc_get_private(dev); \ + \ + k_thread_create(&priv->thread_data, udc_it82xx2_stack_##n, \ + K_THREAD_STACK_SIZEOF(udc_it82xx2_stack_##n), \ + udc_it82xx2_thread_##n, (void *)dev, NULL, NULL, K_PRIO_COOP(8), \ + 0, K_NO_WAIT); \ + k_thread_name_set(&priv->thread_data, dev->name); \ + } \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct udc_ep_config ep_cfg_out[MAX_NUM_ENDPOINTS]; \ + static struct udc_ep_config ep_cfg_in[MAX_NUM_ENDPOINTS]; \ + \ + static struct usb_it82xx2_config udc_cfg_##n = { \ + .base = (struct usb_it82xx2_regs *)DT_INST_REG_ADDR(n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .wuc = {.dev = IT8XXX2_DEV_WUC(0, n), .mask = IT8XXX2_DEV_WUC_MASK(0, n)}, \ + .usb_irq = DT_INST_IRQ_BY_IDX(n, 0, irq), \ + .wu_irq = DT_INST_IRQ_BY_IDX(n, 1, irq), \ + .ep_cfg_in = ep_cfg_out, \ + .ep_cfg_out = ep_cfg_in, \ + .make_thread = udc_it82xx2_make_thread_##n, \ + }; \ + \ + static struct it82xx2_data priv_data_##n = {}; \ + \ + static struct udc_data udc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, it82xx2_usb_driver_preinit, NULL, &udc_data_##n, &udc_cfg_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &it82xx2_api); + +DT_INST_FOREACH_STATUS_OKAY(IT82xx2_USB_DEVICE_DEFINE) diff --git a/drivers/usb/udc/udc_kinetis.c b/drivers/usb/udc/udc_kinetis.c index 38dd1db2d04..08934115b0c 100644 --- a/drivers/usb/udc/udc_kinetis.c +++ b/drivers/usb/udc/udc_kinetis.c @@ -195,10 +195,10 @@ static int usbfsotg_xfer_continue(const struct device *dev, } if (USB_EP_DIR_IS_OUT(cfg->addr)) { - len = MIN(net_buf_tailroom(buf), cfg->mps); + len = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); data_ptr = net_buf_tail(buf); } else { - len = MIN(buf->len, cfg->mps); + len = MIN(buf->len, udc_mps_ep_size(cfg)); data_ptr = buf->data; } @@ -243,7 +243,7 @@ static inline int usbfsotg_ctrl_feed_start(const struct device *dev, } bd = usbfsotg_get_ebd(dev, cfg, false); - length = MIN(net_buf_tailroom(buf), cfg->mps); + length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); priv->out_buf[cfg->stat.odd] = buf; priv->busy[cfg->stat.odd] = true; @@ -267,7 +267,7 @@ static inline int usbfsotg_ctrl_feed_start_next(const struct device *dev, } bd = usbfsotg_get_ebd(dev, cfg, true); - length = MIN(net_buf_tailroom(buf), cfg->mps); + length = MIN(net_buf_tailroom(buf), udc_mps_ep_size(cfg)); priv->out_buf[!cfg->stat.odd] = buf; priv->busy[!cfg->stat.odd] = true; @@ -592,7 +592,8 @@ static ALWAYS_INLINE void isr_handle_xfer_done(const struct device *dev, } net_buf_add(buf, len); - if (net_buf_tailroom(buf) >= ep_cfg->mps && len == ep_cfg->mps) { + if (net_buf_tailroom(buf) >= udc_mps_ep_size(ep_cfg) && + len == udc_mps_ep_size(ep_cfg)) { if (ep == USB_CONTROL_EP_OUT) { usbfsotg_ctrl_feed_start(dev, buf); } else { diff --git a/drivers/usb/udc/udc_mcux_ehci.c b/drivers/usb/udc/udc_mcux_ehci.c new file mode 100644 index 00000000000..3cb0b299905 --- /dev/null +++ b/drivers/usb/udc/udc_mcux_ehci.c @@ -0,0 +1,867 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT nxp_ehci + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "udc_common.h" +#include "usb.h" +#include "usb_device_config.h" +#include "usb_device_mcux_drv_port.h" +#include "usb_device_ehci.h" +#include "usb_phy.h" + +#include +LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); + +/* + * There is no real advantage to change control endpoint size + * but we can use it for testing UDC driver API and higher layers. + */ +#define USB_MCUX_MPS0 UDC_MPS0_64 +#define USB_MCUX_EP0_SIZE 64 + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device) + +struct udc_mcux_config { + const usb_device_controller_interface_struct_t *mcux_if; + void (*irq_enable_func)(const struct device *dev); + void (*irq_disable_func)(const struct device *dev); + size_t num_of_eps; + struct udc_ep_config *ep_cfg_in; + struct udc_ep_config *ep_cfg_out; + uintptr_t base; + const struct pinctrl_dev_config *pincfg; + usb_phy_config_struct_t *phy_config; +}; + +struct udc_mcux_data { + const struct device *dev; + usb_device_struct_t mcux_device; + uint8_t controller_id; /* 0xFF is invalid value */ +}; + +static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, + void *param) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + status = mcux_if->deviceControl(priv->mcux_device.controllerHandle, + command, param); + + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +/* If ep is busy, return busy. Otherwise feed the buf to controller */ +static int udc_mcux_ep_feed(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status = kStatus_USB_Success; + uint8_t *data; + uint32_t len; + unsigned int key; + usb_device_endpoint_status_struct_t ep_status; + + ep_status.endpointAddress = cfg->addr; + udc_mcux_control(dev, kUSB_DeviceControlGetEndpointStatus, &ep_status); + if (ep_status.endpointStatus == kUSB_DeviceEndpointStateStalled) { + return -EACCES; /* stalled */ + } + + key = irq_lock(); + if (!udc_ep_is_busy(dev, cfg->addr)) { + udc_ep_set_busy(dev, cfg->addr, true); + irq_unlock(key); + + if (USB_EP_DIR_IS_OUT(cfg->addr)) { + len = net_buf_tailroom(buf); + data = net_buf_tail(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } else { + len = buf->len; + data = buf->data; + status = mcux_if->deviceSend(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } + + key = irq_lock(); + if (status != kStatus_USB_Success) { + udc_ep_set_busy(dev, cfg->addr, false); + } + irq_unlock(key); + } else { + irq_unlock(key); + return -EBUSY; + } + + return (status == kStatus_USB_Success ? 0 : -EIO); +} + +/* return success if the ep is busy or stalled. */ +static int udc_mcux_ep_try_feed(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *feed_buf; + + feed_buf = udc_buf_peek(dev, cfg->addr); + if (feed_buf) { + int ret = udc_mcux_ep_feed(dev, cfg, feed_buf); + + return ((ret == -EBUSY || ret == -EACCES || ret == 0) ? 0 : -EIO); + } + + return 0; +} + +/* + * Allocate buffer and initiate a new control OUT transfer. + */ +static int udc_mcux_ctrl_feed_dout(const struct device *dev, + const size_t length) +{ + struct net_buf *buf; + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + int ret; + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); + if (buf == NULL) { + return -ENOMEM; + } + + net_buf_put(&cfg->fifo, buf); + + ret = udc_mcux_ep_feed(dev, cfg, buf); + + if (ret) { + net_buf_unref(buf); + return ret; + } + + return 0; +} + +static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup) +{ + int err; + struct net_buf *buf; + + LOG_DBG("setup packet"); + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, + sizeof(struct usb_setup_packet)); + if (buf == NULL) { + LOG_ERR("Failed to allocate for setup"); + return -EIO; + } + + udc_ep_buf_set_setup(buf); + memcpy(buf->data, setup, 8); + net_buf_add(buf, 8); + + if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD && + setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE && + setup->bRequest == USB_SREQ_SET_ADDRESS && + setup->wLength == 0) { + udc_mcux_control(dev, kUSB_DeviceControlPreSetDeviceAddress, + &setup->wValue); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (!buf->len) { + return -EIO; + } + + if (udc_ctrl_stage_is_data_out(dev)) { + /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", buf); + err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf)); + if (err == -ENOMEM) { + err = udc_submit_ep_event(dev, buf, err); + } + } else if (udc_ctrl_stage_is_data_in(dev)) { + err = udc_ctrl_submit_s_in_status(dev); + } else { + err = udc_ctrl_submit_s_status(dev); + } + + return err; +} + +static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); + net_buf_add(buf, len); + if (udc_ctrl_stage_is_status_out(dev)) { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } else { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + } + + if (udc_ctrl_stage_is_status_in(dev)) { + err = udc_ctrl_submit_s_out_status(dev, buf); + } + + return err; +} + +static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + + if (udc_ctrl_stage_is_status_in(dev) || + udc_ctrl_stage_is_no_data(dev)) { + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_out(dev)) { + /* + * IN transfer finished, release buffer, + * control OUT buffer should be already fed. + */ + net_buf_unref(buf); + err = udc_mcux_ctrl_feed_dout(dev, 0u); + } + + return err; +} + +static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); + net_buf_add(buf, len); + + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_out(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (ep == USB_CONTROL_EP_OUT) { + err = udc_mcux_handler_ctrl_out(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_out(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* return true - zlp is feed; false - no zlp */ +static bool udc_mcux_handler_zlt(const struct device *dev, uint8_t ep, struct net_buf *buf, + uint16_t mcux_len) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + + /* The whole transfer is already done by MCUX controller driver. */ + if (mcux_len >= buf->len) { + if (udc_ep_buf_has_zlp(buf)) { + usb_status_t status; + + udc_ep_buf_clear_zlp(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + ep, NULL, 0); + if (status != kStatus_USB_Success) { + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + return false; + } + return true; + } + } + + return false; +} + +static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (udc_mcux_handler_zlt(dev, ep, buf, mcux_len)) { + return 0; + } + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + if (ep == USB_CONTROL_EP_IN) { + err = udc_mcux_handler_ctrl_in(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_in(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* NXP MCUX controller driver notify transfers/status through this interface */ +usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) +{ + usb_device_callback_message_struct_t *mcux_msg = msg; + uint8_t ep; + usb_device_notification_t mcux_notify; + struct udc_mcux_data *priv; + const struct device *dev; + usb_status_t mcux_status = kStatus_USB_Success; + int err = 0; + + if ((NULL == msg) || (NULL == handle)) { + return kStatus_USB_InvalidHandle; + } + + mcux_notify = (usb_device_notification_t)mcux_msg->code; + priv = (struct udc_mcux_data *)(PRV_DATA_HANDLE(handle)); + dev = priv->dev; + + switch (mcux_notify) { + case kUSB_DeviceNotifyBusReset: + struct udc_ep_config *cfg; + + udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL); + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT); + } + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_IN); + } + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + udc_submit_event(dev, UDC_EVT_RESET, 0); + break; + case kUSB_DeviceNotifyError: + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + break; + case kUSB_DeviceNotifySuspend: + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + break; + case kUSB_DeviceNotifyResume: + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + break; + case kUSB_DeviceNotifyLPMSleep: + break; + case kUSB_DeviceNotifyDetach: + udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + break; + case kUSB_DeviceNotifyAttach: + udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + break; + case kUSB_DeviceNotifySOF: + udc_submit_event(dev, UDC_EVT_SOF, 0); + break; + default: + ep = mcux_msg->code; + if (mcux_msg->isSetup) { + struct usb_setup_packet *setup = + (struct usb_setup_packet *)mcux_msg->buffer; + + err = udc_mcux_handler_setup(dev, setup); + } else if (USB_EP_DIR_IS_IN(ep)) { + err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length); + } else { + err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length); + } + + break; + } + + if (unlikely(err)) { + udc_submit_event(dev, UDC_EVT_ERROR, err); + mcux_status = kStatus_USB_Error; + } + return mcux_status; +} + +static void udc_mcux_isr(const struct device *dev) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + + USB_DeviceEhciIsrFunction((void *)(&priv->mcux_device)); +} + +/* Return actual USB device speed */ +static enum udc_bus_speed udc_mcux_device_speed(const struct device *dev) +{ + int err; + uint8_t mcux_speed; + + err = udc_mcux_control(dev, kUSB_DeviceControlGetSpeed, &mcux_speed); + if (err) { + /* + * In the current version of all NXP USB device drivers, + * no error is returned if the parameter is correct. + */ + return UDC_BUS_SPEED_FS; + } + + switch (mcux_speed) { + case USB_SPEED_HIGH: + return UDC_BUS_SPEED_HS; + case USB_SPEED_LOW: + __ASSERT(false, "Low speed mode not supported"); + __fallthrough; + case USB_SPEED_FULL: + __fallthrough; + default: + return UDC_BUS_SPEED_FS; + } +} + +static int udc_mcux_ep_enqueue(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + udc_buf_put(cfg, buf); + if (cfg->stat.halted) { + LOG_DBG("ep 0x%02x halted", cfg->addr); + return 0; + } + + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_dequeue(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *buf; + unsigned int key; + + cfg->stat.halted = false; + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + key = irq_lock(); + udc_ep_set_busy(dev, cfg->addr, false); + irq_unlock(key); + + return 0; +} + +static int udc_mcux_ep_set_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + return udc_mcux_control(dev, kUSB_DeviceControlEndpointStall, &cfg->addr); +} + +static int udc_mcux_ep_clear_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + (void)udc_mcux_control(dev, kUSB_DeviceControlEndpointUnstall, &cfg->addr); + /* transfer is enqueued after stalled */ + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_enable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + usb_device_endpoint_init_struct_t ep_init; + + LOG_DBG("Enable ep 0x%02x", cfg->addr); + + ep_init.zlt = 0U; + ep_init.interval = cfg->interval; + ep_init.endpointAddress = cfg->addr; + ep_init.maxPacketSize = udc_mps_ep_size(cfg); + + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_CONTROL: + ep_init.transferType = USB_ENDPOINT_CONTROL; + break; + case USB_EP_TYPE_BULK: + ep_init.transferType = USB_ENDPOINT_BULK; + break; + case USB_EP_TYPE_INTERRUPT: + ep_init.transferType = USB_ENDPOINT_INTERRUPT; + break; + case USB_EP_TYPE_ISO: + ep_init.transferType = USB_ENDPOINT_ISOCHRONOUS; + break; + default: + return -EINVAL; + } + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointInit, &ep_init); +} + +static int udc_mcux_ep_disable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + LOG_DBG("Disable ep 0x%02x", cfg->addr); + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointDeinit, &cfg->addr); +} + +static int udc_mcux_host_wakeup(const struct device *dev) +{ + return -ENOTSUP; +} + +static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) +{ + uint8_t temp_addr = addr; + + return udc_mcux_control(dev, kUSB_DeviceControlSetDeviceAddress, &temp_addr); +} + +static int udc_mcux_enable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); +} + +static int udc_mcux_disable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); +} + +static int udc_mcux_init(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + if (config->phy_config != NULL) { + USB_EhciPhyInit(priv->controller_id, 0u, + (usb_phy_config_struct_t *)&config->phy_config); + } +#endif + + /* Init MCUX USB device driver. */ + status = mcux_if->deviceInit(priv->controller_id, + &priv->mcux_device, &(priv->mcux_device.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* enable USB interrupt */ + config->irq_enable_func(dev); + + LOG_DBG("Initialized USB controller %x", (uint32_t)config->base); + + return 0; +} + +static int udc_mcux_shutdown(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + /* Disable interrupt */ + config->irq_disable_func(dev); + + /* De-init MCUX USB device driver. */ + status = mcux_if->deviceDeinit(priv->mcux_device.controllerHandle); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static int udc_mcux_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int udc_mcux_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + +static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, + const struct udc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ +#ifdef USBHS_STACK_BASE_ADDRS + uintptr_t usb_base_addrs[] = USBHS_STACK_BASE_ADDRS; +#else + uintptr_t usb_base_addrs[] = USBHS_BASE_ADDRS; +#endif + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ + for (uint8_t i = 0; i < ARRAY_SIZE(usb_base_addrs); i++) { + if (usb_base_addrs[i] == config->base) { + priv->controller_id = kUSB_ControllerEhci0 + i; + break; + } + } +} + +static int udc_mcux_driver_preinit(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + struct udc_data *data = dev->data; + struct udc_mcux_data *priv = data->priv; + int err; + + udc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_out[i].caps.out = 1; + if (i == 0) { + config->ep_cfg_out[i].caps.control = 1; + config->ep_cfg_out[i].caps.mps = 64; + } else { + config->ep_cfg_out[i].caps.bulk = 1; + config->ep_cfg_out[i].caps.interrupt = 1; + config->ep_cfg_out[i].caps.iso = 1; + config->ep_cfg_out[i].caps.mps = 1024; + } + + config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; + err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_in[i].caps.in = 1; + if (i == 0) { + config->ep_cfg_in[i].caps.control = 1; + config->ep_cfg_in[i].caps.mps = 64; + } else { + config->ep_cfg_in[i].caps.bulk = 1; + config->ep_cfg_in[i].caps.interrupt = 1; + config->ep_cfg_in[i].caps.iso = 1; + config->ep_cfg_in[i].caps.mps = 1024; + } + + config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; + err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + /* Requires udc_mcux_host_wakeup() implementation */ + data->caps.rwup = false; + data->caps.mps0 = USB_MCUX_MPS0; + data->caps.hs = true; + priv->dev = dev; + + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + + return 0; +} + +static const struct udc_api udc_mcux_api = { + .device_speed = udc_mcux_device_speed, + .ep_enqueue = udc_mcux_ep_enqueue, + .ep_dequeue = udc_mcux_ep_dequeue, + .ep_set_halt = udc_mcux_ep_set_halt, + .ep_clear_halt = udc_mcux_ep_clear_halt, + .ep_try_config = NULL, + .ep_enable = udc_mcux_ep_enable, + .ep_disable = udc_mcux_ep_disable, + .host_wakeup = udc_mcux_host_wakeup, + .set_address = udc_mcux_set_address, + .enable = udc_mcux_enable, + .disable = udc_mcux_disable, + .init = udc_mcux_init, + .shutdown = udc_mcux_shutdown, + .lock = udc_mcux_lock, + .unlock = udc_mcux_unlock, +}; + +/* EHCI device driver interface */ +static const usb_device_controller_interface_struct_t udc_mcux_if = { + USB_DeviceEhciInit, USB_DeviceEhciDeinit, USB_DeviceEhciSend, + USB_DeviceEhciRecv, USB_DeviceEhciCancel, USB_DeviceEhciControl +}; + +#define UDC_MCUX_PHY_DEFINE(n) \ +static usb_phy_config_struct_t phy_config_##n = { \ + .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ + .TXCAL45DP = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dp_ohms, 0), \ + .TXCAL45DM = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dm_ohms, 0), \ +} + +#define UDC_MCUX_PHY_DEFINE_OR(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (UDC_MCUX_PHY_DEFINE(n)), ()) + +#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (&phy_config_##n), (NULL)) + +#define USB_MCUX_EHCI_DEVICE_DEFINE(n) \ + UDC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void udc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + udc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void udc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static struct udc_ep_config \ + ep_cfg_out##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + static struct udc_ep_config \ + ep_cfg_in##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct udc_mcux_config priv_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = udc_irq_enable_func##n, \ + .irq_disable_func = udc_irq_disable_func##n, \ + .num_of_eps = DT_INST_PROP(n, num_bidir_endpoints), \ + .ep_cfg_in = ep_cfg_in##n, \ + .ep_cfg_out = ep_cfg_out##n, \ + .mcux_if = &udc_mcux_if, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .phy_config = UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + }; \ + \ + static struct udc_mcux_data priv_data_##n = { \ + }; \ + \ + static struct udc_data udc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \ + &udc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &udc_mcux_api); + +DT_INST_FOREACH_STATUS_OKAY(USB_MCUX_EHCI_DEVICE_DEFINE) diff --git a/drivers/usb/udc/udc_mcux_ip3511.c b/drivers/usb/udc/udc_mcux_ip3511.c new file mode 100644 index 00000000000..0ff464054f1 --- /dev/null +++ b/drivers/usb/udc/udc_mcux_ip3511.c @@ -0,0 +1,884 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define DT_DRV_COMPAT nxp_lpcip3511 + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "udc_common.h" +#include "usb.h" +#include "usb_device_config.h" +#include "usb_device_mcux_drv_port.h" +#include "usb_device_lpcip3511.h" +#include "usb_phy.h" + +#include +LOG_MODULE_REGISTER(udc_mcux, CONFIG_UDC_DRIVER_LOG_LEVEL); + +/* + * There is no real advantage to change control endpoint size + * but we can use it for testing UDC driver API and higher layers. + */ +#define USB_MCUX_MPS0 UDC_MPS0_64 +#define USB_MCUX_EP0_SIZE 64 + +#define PRV_DATA_HANDLE(_handle) CONTAINER_OF(_handle, struct udc_mcux_data, mcux_device) + +struct udc_mcux_config { + const usb_device_controller_interface_struct_t *mcux_if; + void (*irq_enable_func)(const struct device *dev); + void (*irq_disable_func)(const struct device *dev); + size_t num_of_eps; + struct udc_ep_config *ep_cfg_in; + struct udc_ep_config *ep_cfg_out; + uintptr_t base; + const struct pinctrl_dev_config *pincfg; + usb_phy_config_struct_t *phy_config; +}; + +struct udc_mcux_data { + const struct device *dev; + usb_device_struct_t mcux_device; + uint8_t controller_id; /* 0xFF is invalid value */ +}; + +static int udc_mcux_control(const struct device *dev, usb_device_control_type_t command, + void *param) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + status = mcux_if->deviceControl(priv->mcux_device.controllerHandle, + command, param); + + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +/* If ep is busy, return busy. Otherwise feed the buf to controller */ +static int udc_mcux_ep_feed(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status = kStatus_USB_Success; + uint8_t *data; + uint32_t len; + unsigned int key; + usb_device_endpoint_status_struct_t ep_status; + + ep_status.endpointAddress = cfg->addr; + udc_mcux_control(dev, kUSB_DeviceControlGetEndpointStatus, &ep_status); + if (ep_status.endpointStatus == kUSB_DeviceEndpointStateStalled) { + return -EACCES; /* stalled */ + } + + key = irq_lock(); + if (!udc_ep_is_busy(dev, cfg->addr)) { + udc_ep_set_busy(dev, cfg->addr, true); + irq_unlock(key); + + if (USB_EP_DIR_IS_OUT(cfg->addr)) { + len = net_buf_tailroom(buf); + data = net_buf_tail(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } else { + len = buf->len; + data = buf->data; + status = mcux_if->deviceSend(priv->mcux_device.controllerHandle, + cfg->addr, data, len); + } + + key = irq_lock(); + if (status != kStatus_USB_Success) { + udc_ep_set_busy(dev, cfg->addr, false); + } + irq_unlock(key); + } else { + irq_unlock(key); + return -EBUSY; + } + + return (status == kStatus_USB_Success ? 0 : -EIO); +} + +/* return success if the ep is busy or stalled. */ +static int udc_mcux_ep_try_feed(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *feed_buf; + + feed_buf = udc_buf_peek(dev, cfg->addr); + if (feed_buf) { + int ret = udc_mcux_ep_feed(dev, cfg, feed_buf); + + return ((ret == -EBUSY || ret == -EACCES || ret == 0) ? 0 : -EIO); + } + + return 0; +} + +/* + * Allocate buffer and initiate a new control OUT transfer. + */ +static int udc_mcux_ctrl_feed_dout(const struct device *dev, + const size_t length) +{ + struct net_buf *buf; + struct udc_ep_config *cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + int ret; + + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, length); + if (buf == NULL) { + return -ENOMEM; + } + + net_buf_put(&cfg->fifo, buf); + + ret = udc_mcux_ep_feed(dev, cfg, buf); + + if (ret) { + net_buf_unref(buf); + return ret; + } + + return 0; +} + +static int udc_mcux_handler_setup(const struct device *dev, struct usb_setup_packet *setup) +{ + int err; + struct net_buf *buf; + + LOG_DBG("setup packet"); + buf = udc_ctrl_alloc(dev, USB_CONTROL_EP_OUT, + sizeof(struct usb_setup_packet)); + if (buf == NULL) { + LOG_ERR("Failed to allocate for setup"); + return -EIO; + } + + udc_ep_buf_set_setup(buf); + memcpy(buf->data, setup, 8); + net_buf_add(buf, 8); + + if (setup->RequestType.type == USB_REQTYPE_TYPE_STANDARD && + setup->RequestType.direction == USB_REQTYPE_DIR_TO_DEVICE && + setup->bRequest == USB_SREQ_SET_ADDRESS && + setup->wLength == 0) { + udc_mcux_control(dev, kUSB_DeviceControlPreSetDeviceAddress, + &setup->wValue); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (!buf->len) { + return -EIO; + } + + if (udc_ctrl_stage_is_data_out(dev)) { + /* Allocate and feed buffer for data OUT stage */ + LOG_DBG("s:%p|feed for -out-", buf); + err = udc_mcux_ctrl_feed_dout(dev, udc_data_stage_length(buf)); + if (err == -ENOMEM) { + err = udc_submit_ep_event(dev, buf, err); + } + } else if (udc_ctrl_stage_is_data_in(dev)) { + err = udc_ctrl_submit_s_in_status(dev); + } else { + err = udc_ctrl_submit_s_status(dev); + } + + return err; +} + +static int udc_mcux_handler_ctrl_out(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); + net_buf_add(buf, len); + if (udc_ctrl_stage_is_status_out(dev)) { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } else { + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + } + + if (udc_ctrl_stage_is_status_in(dev)) { + err = udc_ctrl_submit_s_out_status(dev, buf); + } + + return err; +} + +static int udc_mcux_handler_ctrl_in(const struct device *dev, struct net_buf *buf, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err = 0; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + + if (udc_ctrl_stage_is_status_in(dev) || + udc_ctrl_stage_is_no_data(dev)) { + /* Status stage finished, notify upper layer */ + err = udc_ctrl_submit_status(dev, buf); + } + + /* Update to next stage of control transfer */ + udc_ctrl_update_stage(dev, buf); + + if (udc_ctrl_stage_is_status_out(dev)) { + /* + * IN transfer finished, release buffer, + * control OUT buffer should be already fed. + */ + net_buf_unref(buf); + err = udc_mcux_ctrl_feed_dout(dev, 0u); + } + + return err; +} + +static int udc_mcux_handler_non_ctrl_in(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(buf->len, mcux_len); + buf->data += len; + buf->len -= len; + + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_non_ctrl_out(const struct device *dev, uint8_t ep, + struct net_buf *buf, uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + uint32_t len; + + len = MIN(net_buf_tailroom(buf), mcux_len); + net_buf_add(buf, len); + + err = udc_submit_ep_event(dev, buf, 0); + udc_mcux_ep_try_feed(dev, udc_get_ep_cfg(dev, ep)); + + return err; +} + +static int udc_mcux_handler_out(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (ep == USB_CONTROL_EP_OUT) { + err = udc_mcux_handler_ctrl_out(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_out(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* return true - zlp is feed; false - no zlp */ +static bool udc_mcux_handler_zlt(const struct device *dev, uint8_t ep, struct net_buf *buf, + uint16_t mcux_len) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + + /* The whole transfer is already done by MCUX controller driver. */ + if (mcux_len >= buf->len) { + if (udc_ep_buf_has_zlp(buf)) { + usb_status_t status; + + udc_ep_buf_clear_zlp(buf); + status = mcux_if->deviceRecv(priv->mcux_device.controllerHandle, + ep, NULL, 0); + if (status != kStatus_USB_Success) { + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + return false; + } + return true; + } + } + + return false; +} + +static int udc_mcux_handler_in(const struct device *dev, uint8_t ep, + uint8_t *mcux_buf, uint16_t mcux_len) +{ + int err; + struct net_buf *buf; + unsigned int key; + + buf = udc_buf_peek(dev, ep); + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + + if (udc_mcux_handler_zlt(dev, ep, buf, mcux_len)) { + return 0; + } + + buf = udc_buf_get(dev, ep); + + key = irq_lock(); + udc_ep_set_busy(dev, ep, false); + irq_unlock(key); + + if (buf == NULL) { + udc_submit_event(dev, UDC_EVT_ERROR, -ENOBUFS); + return -ENOBUFS; + } + if (ep == USB_CONTROL_EP_IN) { + err = udc_mcux_handler_ctrl_in(dev, buf, mcux_buf, mcux_len); + } else { + err = udc_mcux_handler_non_ctrl_in(dev, ep, buf, mcux_buf, mcux_len); + } + + return err; +} + +/* NXP MCUX controller driver notify transfers/status through this interface */ +usb_status_t USB_DeviceNotificationTrigger(void *handle, void *msg) +{ + usb_device_callback_message_struct_t *mcux_msg = msg; + uint8_t ep; + usb_device_notification_t mcux_notify; + struct udc_mcux_data *priv; + const struct device *dev; + usb_status_t mcux_status = kStatus_USB_Success; + int err = 0; + + if ((NULL == msg) || (NULL == handle)) { + return kStatus_USB_InvalidHandle; + } + + mcux_notify = (usb_device_notification_t)mcux_msg->code; + priv = (struct udc_mcux_data *)(PRV_DATA_HANDLE(handle)); + dev = priv->dev; + + switch (mcux_notify) { + case kUSB_DeviceNotifyBusReset: + struct udc_ep_config *cfg; + + udc_mcux_control(dev, kUSB_DeviceControlSetDefaultStatus, NULL); + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_OUT); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_OUT); + } + cfg = udc_get_ep_cfg(dev, USB_CONTROL_EP_IN); + if (cfg->stat.enabled) { + udc_ep_disable_internal(dev, USB_CONTROL_EP_IN); + } + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_OUT, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + + if (udc_ep_enable_internal(dev, USB_CONTROL_EP_IN, + USB_EP_TYPE_CONTROL, + USB_MCUX_EP0_SIZE, 0)) { + LOG_ERR("Failed to enable control endpoint"); + return -EIO; + } + udc_submit_event(dev, UDC_EVT_RESET, 0); + break; + case kUSB_DeviceNotifyError: + udc_submit_event(dev, UDC_EVT_ERROR, -EIO); + break; + case kUSB_DeviceNotifySuspend: + udc_set_suspended(dev, true); + udc_submit_event(dev, UDC_EVT_SUSPEND, 0); + break; + case kUSB_DeviceNotifyResume: + udc_set_suspended(dev, false); + udc_submit_event(dev, UDC_EVT_RESUME, 0); + break; + case kUSB_DeviceNotifyLPMSleep: + break; + case kUSB_DeviceNotifyDetach: + udc_submit_event(dev, UDC_EVT_VBUS_REMOVED, 0); + break; + case kUSB_DeviceNotifyAttach: + udc_submit_event(dev, UDC_EVT_VBUS_READY, 0); + break; + case kUSB_DeviceNotifySOF: + udc_submit_event(dev, UDC_EVT_SOF, 0); + break; + default: + ep = mcux_msg->code; + if (mcux_msg->isSetup) { + struct usb_setup_packet *setup = + (struct usb_setup_packet *)mcux_msg->buffer; + + err = udc_mcux_handler_setup(dev, setup); + } else if (USB_EP_DIR_IS_IN(ep)) { + err = udc_mcux_handler_in(dev, ep, mcux_msg->buffer, mcux_msg->length); + } else { + err = udc_mcux_handler_out(dev, ep, mcux_msg->buffer, mcux_msg->length); + } + + break; + } + + if (unlikely(err)) { + udc_submit_event(dev, UDC_EVT_ERROR, err); + mcux_status = kStatus_USB_Error; + } + return mcux_status; +} + +static void udc_mcux_isr(const struct device *dev) +{ + struct udc_mcux_data *priv = udc_get_private(dev); + + USB_DeviceLpcIp3511IsrFunction((void *)(&priv->mcux_device)); +} + +/* Return actual USB device speed */ +static enum udc_bus_speed udc_mcux_device_speed(const struct device *dev) +{ + int err; + uint8_t mcux_speed; + + err = udc_mcux_control(dev, kUSB_DeviceControlGetSpeed, &mcux_speed); + if (err) { + /* + * In the current version of all NXP USB device drivers, + * no error is returned if the parameter is correct. + */ + return UDC_BUS_SPEED_FS; + } + + switch (mcux_speed) { + case USB_SPEED_HIGH: + return UDC_BUS_SPEED_HS; + case USB_SPEED_LOW: + __ASSERT(false, "Low speed mode not supported"); + __fallthrough; + case USB_SPEED_FULL: + __fallthrough; + default: + return UDC_BUS_SPEED_FS; + } +} + +static int udc_mcux_ep_enqueue(const struct device *dev, + struct udc_ep_config *const cfg, + struct net_buf *const buf) +{ + udc_buf_put(cfg, buf); + if (cfg->stat.halted) { + LOG_DBG("ep 0x%02x halted", cfg->addr); + return 0; + } + + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_dequeue(const struct device *dev, + struct udc_ep_config *const cfg) +{ + struct net_buf *buf; + unsigned int key; + + cfg->stat.halted = false; + buf = udc_buf_get_all(dev, cfg->addr); + if (buf) { + udc_submit_ep_event(dev, buf, -ECONNABORTED); + } + + key = irq_lock(); + udc_ep_set_busy(dev, cfg->addr, false); + irq_unlock(key); + + return 0; +} + +static int udc_mcux_ep_set_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + return udc_mcux_control(dev, kUSB_DeviceControlEndpointStall, &cfg->addr); +} + +static int udc_mcux_ep_clear_halt(const struct device *dev, + struct udc_ep_config *const cfg) +{ + (void)udc_mcux_control(dev, kUSB_DeviceControlEndpointUnstall, &cfg->addr); + /* transfer is enqueued after stalled */ + return udc_mcux_ep_try_feed(dev, cfg); +} + +static int udc_mcux_ep_enable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + usb_device_endpoint_init_struct_t ep_init; + + LOG_DBG("Enable ep 0x%02x", cfg->addr); + + ep_init.zlt = 0U; + ep_init.interval = cfg->interval; + ep_init.endpointAddress = cfg->addr; + ep_init.maxPacketSize = udc_mps_ep_size(cfg); + + switch (cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_CONTROL: + ep_init.transferType = USB_ENDPOINT_CONTROL; + break; + case USB_EP_TYPE_BULK: + ep_init.transferType = USB_ENDPOINT_BULK; + break; + case USB_EP_TYPE_INTERRUPT: + ep_init.transferType = USB_ENDPOINT_INTERRUPT; + break; + case USB_EP_TYPE_ISO: + ep_init.transferType = USB_ENDPOINT_ISOCHRONOUS; + break; + default: + return -EINVAL; + } + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointInit, &ep_init); +} + +static int udc_mcux_ep_disable(const struct device *dev, + struct udc_ep_config *const cfg) +{ + LOG_DBG("Disable ep 0x%02x", cfg->addr); + + return udc_mcux_control(dev, kUSB_DeviceControlEndpointDeinit, &cfg->addr); +} + +static int udc_mcux_host_wakeup(const struct device *dev) +{ + return -ENOTSUP; +} + +static int udc_mcux_set_address(const struct device *dev, const uint8_t addr) +{ + uint8_t temp_addr = addr; + + return udc_mcux_control(dev, kUSB_DeviceControlSetDeviceAddress, &temp_addr); +} + +static int udc_mcux_enable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlRun, NULL); +} + +static int udc_mcux_disable(const struct device *dev) +{ + return udc_mcux_control(dev, kUSB_DeviceControlStop, NULL); +} + +static int udc_mcux_init(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + +#ifdef CONFIG_DT_HAS_NXP_USBPHY_ENABLED + if (config->phy_config != NULL) { + USB_EhciPhyInit(priv->controller_id, 0u, + (usb_phy_config_struct_t *)&config->phy_config); + } +#endif + + /* Init MCUX USB device driver. */ + status = mcux_if->deviceInit(priv->controller_id, + &priv->mcux_device, &(priv->mcux_device.controllerHandle)); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + /* enable USB interrupt */ + config->irq_enable_func(dev); + + LOG_DBG("Initialized USB controller %x", (uint32_t)config->base); + + return 0; +} + +static int udc_mcux_shutdown(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + const usb_device_controller_interface_struct_t *mcux_if = config->mcux_if; + struct udc_mcux_data *priv = udc_get_private(dev); + usb_status_t status; + + /* Disable interrupt */ + config->irq_disable_func(dev); + + /* De-init MCUX USB device driver. */ + status = mcux_if->deviceDeinit(priv->mcux_device.controllerHandle); + if (status != kStatus_USB_Success) { + return -ENOMEM; + } + + return 0; +} + +static int udc_mcux_lock(const struct device *dev) +{ + return udc_lock_internal(dev, K_FOREVER); +} + +static int udc_mcux_unlock(const struct device *dev) +{ + return udc_unlock_internal(dev); +} + +static inline void udc_mcux_get_hal_driver_id(struct udc_mcux_data *priv, + const struct udc_mcux_config *config) +{ + /* + * MCUX USB controller drivers use an ID to tell the HAL drivers + * which controller is being used. This part of the code converts + * the base address to the ID value. + */ +#ifdef USB_BASE_ADDRS + uintptr_t ip3511_fs_base[] = USB_BASE_ADDRS; +#endif +#ifdef USBHSD_BASE_ADDRS + uintptr_t ip3511_hs_base[] = USBHSD_BASE_ADDRS; +#endif + + /* get the right controller id */ + priv->controller_id = 0xFFu; /* invalid value */ +#ifdef USB_BASE_ADDRS + for (uint8_t i = 0; i < ARRAY_SIZE(ip3511_fs_base); i++) { + if (ip3511_fs_base[i] == config->base) { + priv->controller_id = kUSB_ControllerLpcIp3511Fs0 + i; + break; + } + } +#endif + +#ifdef USBHSD_BASE_ADDRS + if (priv->controller_id == 0xFF) { + for (uint8_t i = 0; i < ARRAY_SIZE(ip3511_hs_base); i++) { + if (ip3511_hs_base[i] == config->base) { + priv->controller_id = kUSB_ControllerLpcIp3511Hs0 + i; + break; + } + } + } +#endif +} + +static int udc_mcux_driver_preinit(const struct device *dev) +{ + const struct udc_mcux_config *config = dev->config; + struct udc_data *data = dev->data; + struct udc_mcux_data *priv = data->priv; + int err; + + udc_mcux_get_hal_driver_id(priv, config); + if (priv->controller_id == 0xFFu) { + return -ENOMEM; + } + + k_mutex_init(&data->mutex); + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_out[i].caps.out = 1; + if (i == 0) { + config->ep_cfg_out[i].caps.control = 1; + config->ep_cfg_out[i].caps.mps = 64; + } else { + config->ep_cfg_out[i].caps.bulk = 1; + config->ep_cfg_out[i].caps.interrupt = 1; + config->ep_cfg_out[i].caps.iso = 1; + config->ep_cfg_out[i].caps.mps = 1024; + } + + config->ep_cfg_out[i].addr = USB_EP_DIR_OUT | i; + err = udc_register_ep(dev, &config->ep_cfg_out[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + for (int i = 0; i < config->num_of_eps; i++) { + config->ep_cfg_in[i].caps.in = 1; + if (i == 0) { + config->ep_cfg_in[i].caps.control = 1; + config->ep_cfg_in[i].caps.mps = 64; + } else { + config->ep_cfg_in[i].caps.bulk = 1; + config->ep_cfg_in[i].caps.interrupt = 1; + config->ep_cfg_in[i].caps.iso = 1; + config->ep_cfg_in[i].caps.mps = 1024; + } + + config->ep_cfg_in[i].addr = USB_EP_DIR_IN | i; + err = udc_register_ep(dev, &config->ep_cfg_in[i]); + if (err != 0) { + LOG_ERR("Failed to register endpoint"); + return err; + } + } + + /* Requires udc_mcux_host_wakeup() implementation */ + data->caps.rwup = false; + data->caps.mps0 = USB_MCUX_MPS0; + if ((priv->controller_id == kUSB_ControllerLpcIp3511Hs0) || + (priv->controller_id == kUSB_ControllerLpcIp3511Hs1)) { + data->caps.hs = true; + } + priv->dev = dev; + + pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + + return 0; +} + +static const struct udc_api udc_mcux_api = { + .device_speed = udc_mcux_device_speed, + .ep_enqueue = udc_mcux_ep_enqueue, + .ep_dequeue = udc_mcux_ep_dequeue, + .ep_set_halt = udc_mcux_ep_set_halt, + .ep_clear_halt = udc_mcux_ep_clear_halt, + .ep_try_config = NULL, + .ep_enable = udc_mcux_ep_enable, + .ep_disable = udc_mcux_ep_disable, + .host_wakeup = udc_mcux_host_wakeup, + .set_address = udc_mcux_set_address, + .enable = udc_mcux_enable, + .disable = udc_mcux_disable, + .init = udc_mcux_init, + .shutdown = udc_mcux_shutdown, + .lock = udc_mcux_lock, + .unlock = udc_mcux_unlock, +}; + +/* IP3511 device driver interface */ +static const usb_device_controller_interface_struct_t udc_mcux_if = { + USB_DeviceLpc3511IpInit, USB_DeviceLpc3511IpDeinit, USB_DeviceLpc3511IpSend, + USB_DeviceLpc3511IpRecv, USB_DeviceLpc3511IpCancel, USB_DeviceLpc3511IpControl +}; + +#define UDC_MCUX_PHY_DEFINE(n) \ +static usb_phy_config_struct_t phy_config_##n = { \ + .D_CAL = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_d_cal, 0), \ + .TXCAL45DP = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dp_ohms, 0), \ + .TXCAL45DM = DT_PROP_OR(DT_INST_PHANDLE(n, phy_handle), tx_cal_45_dm_ohms, 0), \ +} + +#define UDC_MCUX_PHY_DEFINE_OR(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (UDC_MCUX_PHY_DEFINE(n)), ()) + +#define UDC_MCUX_PHY_CFG_PTR_OR_NULL(n) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), phy_handle), \ + (&phy_config_##n), (NULL)) + +#define USB_MCUX_IP3511_DEVICE_DEFINE(n) \ + UDC_MCUX_PHY_DEFINE_OR(n); \ + \ + static void udc_irq_enable_func##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), \ + DT_INST_IRQ(n, priority), \ + udc_mcux_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + } \ + \ + static void udc_irq_disable_func##n(const struct device *dev) \ + { \ + irq_disable(DT_INST_IRQN(n)); \ + } \ + \ + static struct udc_ep_config \ + ep_cfg_out##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + static struct udc_ep_config \ + ep_cfg_in##n[DT_INST_PROP(n, num_bidir_endpoints)]; \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct udc_mcux_config priv_config_##n = { \ + .base = DT_INST_REG_ADDR(n), \ + .irq_enable_func = udc_irq_enable_func##n, \ + .irq_disable_func = udc_irq_disable_func##n, \ + .num_of_eps = DT_INST_PROP(n, num_bidir_endpoints), \ + .ep_cfg_in = ep_cfg_in##n, \ + .ep_cfg_out = ep_cfg_out##n, \ + .mcux_if = &udc_mcux_if, \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .phy_config = UDC_MCUX_PHY_CFG_PTR_OR_NULL(n), \ + }; \ + \ + static struct udc_mcux_data priv_data_##n = { \ + }; \ + \ + static struct udc_data udc_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(udc_data_##n.mutex), \ + .priv = &priv_data_##n, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(n, udc_mcux_driver_preinit, NULL, \ + &udc_data_##n, &priv_config_##n, \ + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &udc_mcux_api); + +DT_INST_FOREACH_STATUS_OKAY(USB_MCUX_IP3511_DEVICE_DEFINE) diff --git a/drivers/usb/udc/udc_nrf.c b/drivers/usb/udc/udc_nrf.c index f02dfcdc308..2f195611537 100644 --- a/drivers/usb/udc/udc_nrf.c +++ b/drivers/usb/udc/udc_nrf.c @@ -552,7 +552,7 @@ static int udc_nrf_ep_enable(const struct device *dev, uint16_t mps; __ASSERT_NO_MSG(cfg); - mps = (cfg->mps == 0) ? cfg->caps.mps : cfg->mps; + mps = (udc_mps_ep_size(cfg) == 0) ? cfg->caps.mps : udc_mps_ep_size(cfg); nrf_usbd_common_ep_max_packet_size_set(cfg->addr, mps); nrf_usbd_common_ep_enable(cfg->addr); if (!NRF_USBD_EPISO_CHECK(cfg->addr)) { diff --git a/drivers/usb/udc/udc_stm32.c b/drivers/usb/udc/udc_stm32.c index e86893b1b18..c0d0e8847c9 100644 --- a/drivers/usb/udc/udc_stm32.c +++ b/drivers/usb/udc/udc_stm32.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "udc_common.h" @@ -401,7 +400,7 @@ static int udc_stm32_ep_mem_config(const struct device *dev, const struct udc_stm32_config *cfg = dev->config; uint32_t size; - size = MIN(ep->mps, cfg->ep_mps); + size = MIN(udc_mps_ep_size(ep), cfg->ep_mps); if (!enable) { priv->occupied_mem -= size; @@ -465,7 +464,7 @@ static int udc_stm32_ep_mem_config(const struct device *dev, return 0; } - words = MIN(ep->mps, cfg->ep_mps) / 4; + words = MIN(udc_mps_ep_size(ep), cfg->ep_mps) / 4; words = (words <= 64) ? words * 2 : words; if (!enable) { @@ -603,44 +602,43 @@ static int udc_stm32_host_wakeup(const struct device *dev) return 0; } -static inline int eptype2hal(enum usb_dc_ep_transfer_type eptype) -{ - switch (eptype) { - case USB_DC_EP_CONTROL: - return EP_TYPE_CTRL; - case USB_DC_EP_ISOCHRONOUS: - return EP_TYPE_ISOC; - case USB_DC_EP_BULK: - return EP_TYPE_BULK; - case USB_DC_EP_INTERRUPT: - return EP_TYPE_INTR; - default: - return -EINVAL; - } - - return -EINVAL; -} - static int udc_stm32_ep_enable(const struct device *dev, - struct udc_ep_config *ep) + struct udc_ep_config *ep_cfg) { - enum usb_dc_ep_transfer_type type = ep->attributes & USB_EP_TRANSFER_TYPE_MASK; struct udc_stm32_data *priv = udc_get_private(dev); HAL_StatusTypeDef status; + uint8_t ep_type; int ret; - LOG_DBG("Enable ep 0x%02x", ep->addr); + LOG_DBG("Enable ep 0x%02x", ep_cfg->addr); + + switch (ep_cfg->attributes & USB_EP_TRANSFER_TYPE_MASK) { + case USB_EP_TYPE_CONTROL: + ep_type = EP_TYPE_CTRL; + break; + case USB_EP_TYPE_BULK: + ep_type = EP_TYPE_BULK; + break; + case USB_EP_TYPE_INTERRUPT: + ep_type = EP_TYPE_INTR; + break; + case USB_EP_TYPE_ISO: + ep_type = EP_TYPE_ISOC; + break; + default: + return -EINVAL; + } - ret = udc_stm32_ep_mem_config(dev, ep, true); + ret = udc_stm32_ep_mem_config(dev, ep_cfg, true); if (ret) { return ret; } - status = HAL_PCD_EP_Open(&priv->pcd, ep->addr, ep->mps, - eptype2hal(type)); + status = HAL_PCD_EP_Open(&priv->pcd, ep_cfg->addr, + udc_mps_ep_size(ep_cfg), ep_type); if (status != HAL_OK) { LOG_ERR("HAL_PCD_EP_Open failed(0x%02x), %d", - ep->addr, (int)status); + ep_cfg->addr, (int)status); return -EIO; } diff --git a/drivers/usb/udc/udc_virtual.c b/drivers/usb/udc/udc_virtual.c index d1ba034a2df..cc21649ad67 100644 --- a/drivers/usb/udc/udc_virtual.c +++ b/drivers/usb/udc/udc_virtual.c @@ -183,7 +183,7 @@ static int vrt_handle_out(const struct device *dev, LOG_DBG("Handle data OUT, %zu | %zu", pkt->length, net_buf_tailroom(buf)); - if (net_buf_tailroom(buf) == 0 || pkt->length < ep_cfg->mps) { + if (net_buf_tailroom(buf) == 0 || pkt->length < udc_mps_ep_size(ep_cfg)) { buf = udc_buf_get(dev, ep); if (ep == USB_CONTROL_EP_OUT) { @@ -246,13 +246,13 @@ static int vrt_handle_in(const struct device *dev, } LOG_DBG("Handle data IN, %zu | %u | %u", - pkt->length, buf->len, ep_cfg->mps); + pkt->length, buf->len, udc_mps_ep_size(ep_cfg)); min_len = MIN(pkt->length, buf->len); memcpy(pkt->data, buf->data, min_len); net_buf_pull(buf, min_len); pkt->length = min_len; - if (buf->len == 0 || pkt->length < ep_cfg->mps) { + if (buf->len == 0 || pkt->length < udc_mps_ep_size(ep_cfg)) { if (udc_ep_buf_has_zlp(buf)) { udc_ep_buf_clear_zlp(buf); goto continue_in; diff --git a/include/zephyr/drivers/usb/udc.h b/include/zephyr/drivers/usb/udc.h index 2e4d4dbcf32..b0a136fac62 100644 --- a/include/zephyr/drivers/usb/udc.h +++ b/include/zephyr/drivers/usb/udc.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include @@ -76,6 +76,8 @@ struct udc_ep_caps { uint32_t bulk : 1; /** ISO transfer capable endpoint */ uint32_t iso : 1; + /** High-Bandwidth (interrupt or iso) capable endpoint */ + uint32_t high_bandwidth : 1; /** IN transfer capable endpoint */ uint32_t in : 1; /** OUT transfer capable endpoint */ @@ -702,6 +704,18 @@ static inline struct udc_buf_info *udc_get_buf_info(const struct net_buf *const return (struct udc_buf_info *)net_buf_user_data(buf); } +/** + * @brief Get endpoint size from UDC endpoint configuration + * + * @param[in] cfg Pointer to UDC endpoint configuration + * + * @return Endpoint size + */ +static inline uint16_t udc_mps_ep_size(const struct udc_ep_config *const cfg) +{ + return USB_MPS_EP_SIZE(cfg->mps); +} + /** * @} */ diff --git a/include/zephyr/drivers/usb/udc_buf.h b/include/zephyr/drivers/usb/udc_buf.h new file mode 100644 index 00000000000..69b85da94db --- /dev/null +++ b/include/zephyr/drivers/usb/udc_buf.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Buffers for USB device support + */ + +#ifndef ZEPHYR_INCLUDE_UDC_BUF_H +#define ZEPHYR_INCLUDE_UDC_BUF_H + +#include +#include + +#if defined(CONFIG_DCACHE) && !defined(CONFIG_UDC_BUF_FORCE_NOCACHE) +/* + * Here we try to get DMA-safe buffers, but we lack a consistent source of + * information about data cache properties, such as line cache size, and a + * consistent source of information about what part of memory is DMA'able. + * For now, we simply assume that all available memory is DMA'able and use + * Kconfig option DCACHE_LINE_SIZE for alignment and granularity. + */ +#define Z_UDC_BUF_ALIGN CONFIG_DCACHE_LINE_SIZE +#define Z_UDC_BUF_GRANULARITY CONFIG_DCACHE_LINE_SIZE +#else +/* + * Default alignment and granularity to pointer size if the platform does not + * have a data cache or buffers are placed in nocache memory region. + */ +#define Z_UDC_BUF_ALIGN sizeof(void *) +#define Z_UDC_BUF_GRANULARITY sizeof(void *) +#endif + +/** + * @brief Buffer macros and definitions used in USB device support + * @defgroup udc_buf Buffer macros and definitions used in USB device support + * @ingroup usb + * @{ + */ + +/** Buffer alignment required by the UDC driver */ +#define UDC_BUF_ALIGN Z_UDC_BUF_ALIGN + +/** Buffer granularity required by the UDC driver */ +#define UDC_BUF_GRANULARITY Z_UDC_BUF_GRANULARITY + +/** + * @brief Define a UDC driver-compliant static buffer + * + * This macro should be used if the application defines its own buffers to be + * used for USB transfers. + * + * @param name Buffer name + * @param size Buffer size + */ +#define UDC_STATIC_BUF_DEFINE(name, size) \ + static uint8_t __aligned(UDC_BUF_ALIGN) name[ROUND_UP(size, UDC_BUF_GRANULARITY)]; + +/** + * @brief Verify that the buffer is aligned as required by the UDC driver + * + * @see IS_ALIGNED + * + * @param buf Buffer pointer + */ +#define IS_UDC_ALIGNED(buf) IS_ALIGNED(buf, UDC_BUF_ALIGN) + +/** + * @cond INTERNAL_HIDDEN + */ +#define UDC_HEAP_DEFINE(name, bytes, in_section) \ + uint8_t in_section __aligned(UDC_BUF_ALIGN) \ + kheap_##name[MAX(bytes, Z_HEAP_MIN_SIZE)]; \ + STRUCT_SECTION_ITERABLE(k_heap, name) = { \ + .heap = { \ + .init_mem = kheap_##name, \ + .init_bytes = MAX(bytes, Z_HEAP_MIN_SIZE), \ + }, \ + } + +#define UDC_K_HEAP_DEFINE(name, size) \ + COND_CODE_1(CONFIG_UDC_BUF_FORCE_NOCACHE, \ + (UDC_HEAP_DEFINE(name, size, __nocache)), \ + (UDC_HEAP_DEFINE(name, size, __noinit))) + +extern const struct net_buf_data_cb net_buf_dma_cb; +/** @endcond */ + +/** + * @brief Define a new pool for UDC buffers with variable-size payloads + * + * This macro is similar to `NET_BUF_POOL_VAR_DEFINE`, but provides buffers + * with alignment and granularity suitable for use by UDC driver. + * + * @see NET_BUF_POOL_VAR_DEFINE + * + * @param pname Name of the pool variable. + * @param count Number of buffers in the pool. + * @param size Maximum data payload per buffer. + * @param ud_size User data space to reserve per buffer. + * @param fdestroy Optional destroy callback when buffer is freed. + */ +#define UDC_BUF_POOL_VAR_DEFINE(pname, count, size, ud_size, fdestroy) \ + _NET_BUF_ARRAY_DEFINE(pname, count, ud_size); \ + UDC_K_HEAP_DEFINE(net_buf_mem_pool_##pname, size); \ + static const struct net_buf_data_alloc net_buf_data_alloc_##pname = { \ + .cb = &net_buf_dma_cb, \ + .alloc_data = &net_buf_mem_pool_##pname, \ + .max_alloc_size = 0, \ + }; \ + static STRUCT_SECTION_ITERABLE(net_buf_pool, pname) = \ + NET_BUF_POOL_INITIALIZER(pname, &net_buf_data_alloc_##pname, \ + _net_buf_##pname, count, ud_size, \ + fdestroy) + +/** + * @brief Define a new pool for UDC buffers based on fixed-size data + * + * This macro is similar to `NET_BUF_POOL_DEFINE`, but provides buffers + * with alignment and granularity suitable for use by UDC driver. + * + * @see NET_BUF_POOL_DEFINE + + * @param pname Name of the pool variable. + * @param count Number of buffers in the pool. + * @param size Maximum data payload per buffer. + * @param ud_size User data space to reserve per buffer. + * @param fdestroy Optional destroy callback when buffer is freed. + */ +#define UDC_BUF_POOL_DEFINE(pname, count, size, ud_size, fdestroy) \ + _NET_BUF_ARRAY_DEFINE(pname, count, ud_size); \ + BUILD_ASSERT((UDC_BUF_GRANULARITY) % (UDC_BUF_ALIGN) == 0, \ + "Code assumes granurality is multiple of alignment"); \ + static uint8_t __nocache __aligned(UDC_BUF_ALIGN) \ + net_buf_data_##pname[count][ROUND_UP(size, UDC_BUF_GRANULARITY)];\ + static const struct net_buf_pool_fixed net_buf_fixed_##pname = { \ + .data_pool = (uint8_t *)net_buf_data_##pname, \ + }; \ + static const struct net_buf_data_alloc net_buf_fixed_alloc_##pname = { \ + .cb = &net_buf_fixed_cb, \ + .alloc_data = (void *)&net_buf_fixed_##pname, \ + .max_alloc_size = ROUND_UP(size, UDC_BUF_GRANULARITY), \ + }; \ + static STRUCT_SECTION_ITERABLE(net_buf_pool, pname) = \ + NET_BUF_POOL_INITIALIZER(pname, &net_buf_fixed_alloc_##pname, \ + _net_buf_##pname, count, ud_size, \ + fdestroy) + +/** + * @} + */ + +#endif /* ZEPHYR_INCLUDE_UDC_BUF_H */ diff --git a/include/zephyr/input/input.h b/include/zephyr/input/input.h index 32e9bf9fa8e..a58feecbd95 100644 --- a/include/zephyr/input/input.h +++ b/include/zephyr/input/input.h @@ -124,7 +124,9 @@ struct input_callback { /** @ref device pointer or NULL. */ const struct device *dev; /** The callback function. */ - void (*callback)(struct input_event *evt); + void (*callback)(struct input_event *evt, void *user_data); + /** User data pointer. */ + void *user_data; }; /** @@ -136,12 +138,14 @@ struct input_callback { * * @param _dev @ref device pointer or NULL. * @param _callback The callback function. + * @param _user_data Pointer to user specified data. */ -#define INPUT_CALLBACK_DEFINE(_dev, _callback) \ +#define INPUT_CALLBACK_DEFINE(_dev, _callback, _user_data) \ static const STRUCT_SECTION_ITERABLE(input_callback, \ _input_callback__##_callback) = { \ .dev = _dev, \ .callback = _callback, \ + .user_data = _user_data, \ } #ifdef __cplusplus diff --git a/include/zephyr/usb/usb_ch9.h b/include/zephyr/usb/usb_ch9.h index 8fe33f46b71..b486466cb9c 100644 --- a/include/zephyr/usb/usb_ch9.h +++ b/include/zephyr/usb/usb_ch9.h @@ -348,6 +348,29 @@ struct usb_association_descriptor { /** Calculate high speed isochronous endpoint bInterval from a value in microseconds */ #define USB_HS_ISO_EP_INTERVAL(us) CLAMP((ilog2((us) / 125U) + 1U), 1U, 16U) +/** Get endpoint size field from Max Packet Size value */ +#define USB_MPS_EP_SIZE(mps) ((mps) & BIT_MASK(11)) + +/** Get number of additional transactions per microframe from Max Packet Size value */ +#define USB_MPS_ADDITIONAL_TRANSACTIONS(mps) (((mps) & 0x1800) >> 11) + +/** Calculate total payload length from Max Packet Size value */ +#define USB_MPS_TO_TPL(mps) \ + ((1 + USB_MPS_ADDITIONAL_TRANSACTIONS(mps)) * USB_MPS_EP_SIZE(mps)) + +/** Calculate Max Packet Size value from total payload length */ +#define USB_TPL_TO_MPS(tpl) \ + (((tpl) > 2048) ? ((2 << 11) | ((tpl) / 3)) : \ + ((tpl) > 1024) ? ((1 << 11) | ((tpl) / 2)) : \ + (tpl)) + +/** Determine whether total payload length value is valid according to USB 2.0 */ +#define USB_TPL_IS_VALID(tpl) \ + (((tpl) > 3072) ? false : \ + ((tpl) > 2048) ? ((tpl) % 3 == 0) : \ + ((tpl) > 1024) ? ((tpl) % 2 == 0) : \ + ((tpl) >= 0)) + #ifdef __cplusplus } #endif diff --git a/include/zephyr/usb/usbd.h b/include/zephyr/usb/usbd.h index 7afc27656ee..b4a5e20fcc1 100644 --- a/include/zephyr/usb/usbd.h +++ b/include/zephyr/usb/usbd.h @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include @@ -67,6 +67,7 @@ enum usbd_str_desc_utype { USBD_DUT_STRING_MANUFACTURER, USBD_DUT_STRING_PRODUCT, USBD_DUT_STRING_SERIAL_NUMBER, + USBD_DUT_STRING_CONFIG, USBD_DUT_STRING_INTERFACE, }; @@ -131,6 +132,8 @@ struct usbd_config_node { sys_snode_t node; /** Pointer to configuration descriptor */ void *desc; + /** Optional pointer to string descriptor node */ + struct usbd_desc_node *str_desc_nd; /** List of registered classes (functions) */ sys_slist_t class_list; }; @@ -388,7 +391,27 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c return c_data->priv; } -#define USBD_DEVICE_DEFINE(device_name, uhc_dev, vid, pid) \ +/** + * @brief Define USB device context structure + * + * Macro defines a USB device structure needed by the stack to manage its + * properties and runtime data. The @p vid and @p pid parameters can also be + * changed using usbd_device_set_vid() and usbd_device_set_pid(). + * + * Example of use: + * + * @code{.c} + * USBD_DEVICE_DEFINE(sample_usbd, + * DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + * YOUR_VID, YOUR_PID); + * @endcode + * + * @param device_name USB device context name + * @param udc_dev Pointer to UDC device structure + * @param vid Vendor ID + * @param pid Product ID + */ +#define USBD_DEVICE_DEFINE(device_name, udc_dev, vid, pid) \ static struct usb_device_descriptor \ fs_desc_##device_name = { \ .bLength = sizeof(struct usb_device_descriptor), \ @@ -425,12 +448,29 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c }; \ static STRUCT_SECTION_ITERABLE(usbd_context, device_name) = { \ .name = STRINGIFY(device_name), \ - .dev = uhc_dev, \ + .dev = udc_dev, \ .fs_desc = &fs_desc_##device_name, \ .hs_desc = &hs_desc_##device_name, \ } -#define USBD_CONFIGURATION_DEFINE(name, attrib, power) \ +/** + * @brief Define USB device configuration + * + * USB device requires at least one configuration instance per supported speed. + * @p attrib is a combination of `USB_SCD_SELF_POWERED` or `USB_SCD_REMOTE_WAKEUP`, + * depending on which characteristic the USB device should have in this + * configuration. + * + * @param name Configuration name + * @param attrib Configuration characteristics. Attributes can also be updated + * with usbd_config_attrib_rwup() and usbd_config_attrib_self() + * @param power bMaxPower value in 2 mA units. This value can also be set with + * usbd_config_maxpower() + * @param desc_nd Address of the string descriptor node used to describe the + * configuration, see USBD_DESC_CONFIG_DEFINE(). + * String descriptors are optional and the parameter can be NULL. + */ +#define USBD_CONFIGURATION_DEFINE(name, attrib, power, desc_nd) \ static struct usb_cfg_descriptor \ cfg_desc_##name = { \ .bLength = sizeof(struct usb_cfg_descriptor), \ @@ -445,6 +485,7 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c BUILD_ASSERT((power) < 256, "Too much power"); \ static struct usbd_config_node name = { \ .desc = &cfg_desc_##name, \ + .str_desc_nd = desc_nd, \ } /** @@ -463,13 +504,13 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c #define USBD_DESC_LANG_DEFINE(name) \ static uint16_t langid_##name = sys_cpu_to_le16(0x0409); \ static struct usbd_desc_node name = { \ - .bLength = sizeof(struct usb_string_descriptor), \ - .bDescriptorType = USB_DESC_STRING, \ .str = { \ .idx = 0, \ .utype = USBD_DUT_STRING_LANG, \ }, \ .ptr = &langid_##name, \ + .bLength = sizeof(struct usb_string_descriptor), \ + .bDescriptorType = USB_DESC_STRING, \ } /** @@ -490,9 +531,9 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .utype = d_utype, \ .ascii7 = true, \ }, \ + .ptr = &ascii_##d_name, \ .bLength = USB_STRING_DESCRIPTOR_LENGTH(d_string), \ .bDescriptorType = USB_DESC_STRING, \ - .ptr = &ascii_##d_name, \ } /** @@ -543,6 +584,18 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .bDescriptorType = USB_DESC_STRING, \ } +/** + * @brief Create a string descriptor node for configuration descriptor + * + * This macro defines a descriptor node whose address can be used as an + * argument for the USBD_CONFIGURATION_DEFINE() macro. + * + * @param d_name String descriptor node identifier. + * @param d_string ASCII7 encoded configuration description string literal + */ +#define USBD_DESC_CONFIG_DEFINE(d_name, d_string) \ + USBD_DESC_STRING_DEFINE(d_name, d_string, USBD_DUT_STRING_CONFIG) + /** * @brief Define BOS Device Capability descriptor node * @@ -563,6 +616,17 @@ static inline void *usbd_class_get_private(const struct usbd_class_data *const c .bDescriptorType = USB_DESC_BOS, \ } +/** + * @brief Define USB device support class data + * + * Macro defines class (function) data, as well as corresponding node + * structures used internally by the stack. + * + * @param class_name Class name + * @param class_api Pointer to struct usbd_class_api + * @param class_priv Class private data + * @param class_v_reqs Pointer to struct usbd_cctx_vendor_req + */ #define USBD_DEFINE_CLASS(class_name, class_api, class_priv, class_v_reqs) \ static struct usbd_class_data class_name = { \ .name = STRINGIFY(class_name), \ @@ -660,7 +724,7 @@ int usbd_add_configuration(struct usbd_context *uds_ctx, * @param[in] uds_ctx Pointer to USB device support context * @param[in] name Class instance name * @param[in] speed Configuration speed - * @param[in] cfg Configuration value (similar to bConfigurationValue) + * @param[in] cfg Configuration value (bConfigurationValue) * * @return 0 on success, other values on fail. */ @@ -668,6 +732,25 @@ int usbd_register_class(struct usbd_context *uds_ctx, const char *name, const enum usbd_speed speed, uint8_t cfg); +/** + * @brief Register all available USB class instances + * + * Register all available instances. Like usbd_register_class, but does not + * take the instance name and instead registers all available instances. + * + * @note This cannot be combined. If your application calls + * usbd_register_class for any device, configuration number, or instance, + * either usbd_register_class or this function will fail. + * + * @param[in] uds_ctx Pointer to USB device support context + * @param[in] speed Configuration speed + * @param[in] cfg Configuration value (bConfigurationValue) + * + * @return 0 on success, other values on fail. + */ +int usbd_register_all_classes(struct usbd_context *uds_ctx, + const enum usbd_speed speed, uint8_t cfg); + /** * @brief Unregister an USB class instance * @@ -678,7 +761,7 @@ int usbd_register_class(struct usbd_context *uds_ctx, * @param[in] uds_ctx Pointer to USB device support context * @param[in] name Class instance name * @param[in] speed Configuration speed - * @param[in] cfg Configuration value (similar to bConfigurationValue) + * @param[in] cfg Configuration value (bConfigurationValue) * * @return 0 on success, other values on fail. */ @@ -686,6 +769,21 @@ int usbd_unregister_class(struct usbd_context *uds_ctx, const char *name, const enum usbd_speed speed, uint8_t cfg); +/** + * @brief Unregister all available USB class instances + * + * Unregister all available instances. Like usbd_unregister_class, but does not + * take the instance name and instead unregisters all available instances. + * + * @param[in] uds_ctx Pointer to USB device support context + * @param[in] speed Configuration speed + * @param[in] cfg Configuration value (bConfigurationValue) + * + * @return 0 on success, other values on fail. + */ +int usbd_unregister_all_classes(struct usbd_context *uds_ctx, + const enum usbd_speed speed, uint8_t cfg); + /** * @brief Register USB notification message callback * diff --git a/include/zephyr/usb/usbd_msg.h b/include/zephyr/usb/usbd_msg.h index a11cff7d1da..7f2f888e3f8 100644 --- a/include/zephyr/usb/usbd_msg.h +++ b/include/zephyr/usb/usbd_msg.h @@ -40,6 +40,8 @@ enum usbd_msg_type { USBD_MSG_SUSPEND, /** Bus reset detected */ USBD_MSG_RESET, + /** Device changed configuration */ + USBD_MSG_CONFIGURATION, /** Non-correctable UDC error message */ USBD_MSG_UDC_ERROR, /** Unrecoverable device stack error message */ @@ -59,6 +61,7 @@ static const char *const usbd_msg_type_list[] = { "Device resumed", "Device suspended", "Bus reset", + "New device configuration", "Controller error", "Stack error", "CDC ACM line coding", diff --git a/modules/hal_nxp/CMakeLists.txt b/modules/hal_nxp/CMakeLists.txt index 2f2e53d22ea..f02499cbc43 100644 --- a/modules/hal_nxp/CMakeLists.txt +++ b/modules/hal_nxp/CMakeLists.txt @@ -1,5 +1,6 @@ # # Copyright (c) 2021 Linaro, Limited +# Copyright 2024 NXP # # SPDX-License-Identifier: Apache-2.0 # @@ -7,6 +8,7 @@ if(CONFIG_HAS_MCUX OR CONFIG_HAS_IMX_HAL OR CONFIG_HAS_NXP_S32_HAL) add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR} hal_nxp) add_subdirectory_ifdef(CONFIG_USB_DEVICE_DRIVER usb) + add_subdirectory_ifdef(CONFIG_UDC_DRIVER usb) zephyr_sources_ifdef(CONFIG_PWM_MCUX_CTIMER ${ZEPHYR_CURRENT_MODULE_DIR}/mcux/mcux-sdk/drivers/ctimer/fsl_ctimer.c) zephyr_include_directories_ifdef(CONFIG_PWM_MCUX_CTIMER @@ -22,5 +24,6 @@ if(CONFIG_HAS_MCUX OR CONFIG_HAS_IMX_HAL OR CONFIG_HAS_NXP_S32_HAL) if(CONFIG_NOCACHE_MEMORY) zephyr_compile_definitions_ifdef(CONFIG_USB_DEVICE_DRIVER DATA_SECTION_IS_CACHEABLE=1) + zephyr_compile_definitions_ifdef(CONFIG_UDC_DRIVER DATA_SECTION_IS_CACHEABLE=1) endif() endif() diff --git a/modules/hal_nxp/usb/CMakeLists.txt b/modules/hal_nxp/usb/CMakeLists.txt index 380aa3aef87..da427e3601b 100644 --- a/modules/hal_nxp/usb/CMakeLists.txt +++ b/modules/hal_nxp/usb/CMakeLists.txt @@ -1,6 +1,9 @@ # -# Copyright (c) 2021, NXP +# Copyright (c) 2021,2024 NXP # # SPDX-License-Identifier: Apache-2.0 # zephyr_include_directories(.) +if(CONFIG_DT_HAS_NXP_USBPHY_ENABLED) +zephyr_include_directories(${ZEPHYR_HAL_NXP_MODULE_DIR}/mcux/middleware/mcux-sdk-middleware-usb/phy) +endif() diff --git a/modules/hal_nxp/usb/usb_device_config.h b/modules/hal_nxp/usb/usb_device_config.h index 31a62ffbfeb..78fc2a6303f 100644 --- a/modules/hal_nxp/usb/usb_device_config.h +++ b/modules/hal_nxp/usb/usb_device_config.h @@ -13,6 +13,7 @@ /****************************************************************************** * Definitions *****************************************************************************/ +#ifdef CONFIG_USB_DEVICE_DRIVER /* EHCI instance count */ #ifdef CONFIG_USB_DC_NXP_EHCI #define USB_DEVICE_CONFIG_EHCI (1U) @@ -51,5 +52,62 @@ BUILD_ASSERT(NUM_INSTS <= 1, "Only one USB device supported"); /* Number of endpoints supported */ #define USB_DEVICE_CONFIG_ENDPOINTS (DT_INST_PROP(0, num_bidir_endpoints)) +#else + +#ifdef CONFIG_UDC_NXP_EHCI +#define USB_DEVICE_CONFIG_EHCI (DT_NUM_INST_STATUS_OKAY(nxp_ehci)) +#endif + +#ifdef CONFIG_UDC_NXP_IP3511 + +#if defined(USBHSD_BASE_ADDRS) && defined(USB_BASE_ADDRS) +#define USB_DEVICE_CONFIG_LPCIP3511HS (1U) +#define USB_DEVICE_CONFIG_LPCIP3511FS (1U) + +#else + +#ifdef USBHSD_BASE_ADDRS +#define USB_DEVICE_CONFIG_LPCIP3511HS (DT_NUM_INST_STATUS_OKAY(nxp_lpcip3511)) +#else +#define USB_DEVICE_CONFIG_LPCIP3511HS (0U) +#endif + +#ifdef USB_BASE_ADDRS +#define USB_DEVICE_CONFIG_LPCIP3511FS (DT_NUM_INST_STATUS_OKAY(nxp_lpcip3511)) +#else +#define USB_DEVICE_CONFIG_LPCIP3511FS (0U) +#endif + +#endif +#endif + +/* calculte the num of endponts. + * mcux ip3511 driver doesn't use USB_DEVICE_CONFIG_ENDPOINTS, + * so use ehci endpoint number if ehci is enabled. + */ +#if DT_HAS_COMPAT_STATUS_OKAY(nxp_ehci) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_ehci +#elif DT_HAS_COMPAT_STATUS_OKAY(nxp_lpcip3511) +#undef DT_DRV_COMPAT +#define DT_DRV_COMPAT nxp_lpcip3511 +#endif + +/* Number of endpoints supported */ +#define USB_DEVICE_CONFIG_ENDPOINTS (DT_INST_PROP(0, num_bidir_endpoints)) + +#define USB_DEVICE_CONFIG_SELF_POWER (1U) + +#if ((defined(USB_DEVICE_CONFIG_EHCI)) && (USB_DEVICE_CONFIG_EHCI > 0U)) +/*! @brief How many the DTD are supported. */ +#define USB_DEVICE_CONFIG_EHCI_MAX_DTD (16U) +#endif + +/* TODO: After Kconfig item that enable/disable sof is added, + * use the Kconfig item to control this macro. + */ +#define USB_DEVICE_CONFIG_SOF_NOTIFICATIONS (1U) + +#endif #endif /* __USB_DEVICE_CONFIG_H__ */ diff --git a/modules/lvgl/include/lvgl_common_input.h b/modules/lvgl/include/lvgl_common_input.h index c34dabe1a8a..2d553e6dbdd 100644 --- a/modules/lvgl/include/lvgl_common_input.h +++ b/modules/lvgl/include/lvgl_common_input.h @@ -36,11 +36,8 @@ int lvgl_init_input_devices(void); #define LVGL_KEY_VALID(key) IN_RANGE(key, 0, UINT8_MAX) #define LVGL_INPUT_DEFINE(inst, type, msgq_size, process_evt_cb) \ - static void lvgl_input_cb_##_##inst(struct input_event *evt) \ - { \ - process_evt_cb(DEVICE_DT_INST_GET(inst), evt); \ - } \ - INPUT_CALLBACK_DEFINE(LVGL_INPUT_DEVICE(inst), lvgl_input_cb_##_##inst); \ + INPUT_CALLBACK_DEFINE(LVGL_INPUT_DEVICE(inst), process_evt_cb, \ + (void *)DEVICE_DT_INST_GET(inst)); \ K_MSGQ_DEFINE(lvgl_input_msgq_##type##_##inst, sizeof(lv_indev_data_t), msgq_size, 4) #ifdef __cplusplus diff --git a/modules/lvgl/input/lvgl_button_input.c b/modules/lvgl/input/lvgl_button_input.c index 8ac96b7fed5..9976df48ca7 100644 --- a/modules/lvgl/input/lvgl_button_input.c +++ b/modules/lvgl/input/lvgl_button_input.c @@ -20,8 +20,9 @@ struct lvgl_button_input_config { const lv_coord_t *coordinates; }; -static void lvgl_button_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_button_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct lvgl_common_input_data *data = dev->data; const struct lvgl_button_input_config *cfg = dev->config; uint8_t i; diff --git a/modules/lvgl/input/lvgl_encoder_input.c b/modules/lvgl/input/lvgl_encoder_input.c index b68ca67b871..4191dc7ab99 100644 --- a/modules/lvgl/input/lvgl_encoder_input.c +++ b/modules/lvgl/input/lvgl_encoder_input.c @@ -19,8 +19,9 @@ struct lvgl_encoder_input_config { int button_input_code; }; -static void lvgl_encoder_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_encoder_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct lvgl_common_input_data *data = dev->data; const struct lvgl_encoder_input_config *cfg = dev->config; diff --git a/modules/lvgl/input/lvgl_keypad_input.c b/modules/lvgl/input/lvgl_keypad_input.c index fb131f47a29..963784df096 100644 --- a/modules/lvgl/input/lvgl_keypad_input.c +++ b/modules/lvgl/input/lvgl_keypad_input.c @@ -21,8 +21,9 @@ struct lvgl_keypad_input_config { uint8_t num_codes; }; -static void lvgl_keypad_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_keypad_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; struct lvgl_common_input_data *data = dev->data; const struct lvgl_keypad_input_config *cfg = dev->config; uint8_t i; diff --git a/modules/lvgl/input/lvgl_pointer_input.c b/modules/lvgl/input/lvgl_pointer_input.c index e1b12243750..4bc40db05c4 100644 --- a/modules/lvgl/input/lvgl_pointer_input.c +++ b/modules/lvgl/input/lvgl_pointer_input.c @@ -27,8 +27,9 @@ struct lvgl_pointer_input_data { uint32_t point_y; }; -static void lvgl_pointer_process_event(const struct device *dev, struct input_event *evt) +static void lvgl_pointer_process_event(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; const struct lvgl_pointer_input_config *cfg = dev->config; struct lvgl_pointer_input_data *data = dev->data; lv_disp_t *disp = lv_disp_get_default(); diff --git a/samples/subsys/usb/common/sample_usbd_init.c b/samples/subsys/usb/common/sample_usbd_init.c index 14fd43993e6..768b7550a5a 100644 --- a/samples/subsys/usb/common/sample_usbd_init.c +++ b/samples/subsys/usb/common/sample_usbd_init.c @@ -9,34 +9,49 @@ #include #include #include -#include #include LOG_MODULE_REGISTER(usbd_sample_config); #define ZEPHYR_PROJECT_USB_VID 0x2fe3 +/* doc device instantiation start */ +/* + * Instantiate a context named sample_usbd using the default USB device + * controller, the Zephyr project vendor ID, and the sample product ID. + * Zephyr project vendor ID must not be used outside of Zephyr samples. + */ USBD_DEVICE_DEFINE(sample_usbd, DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), ZEPHYR_PROJECT_USB_VID, CONFIG_SAMPLE_USBD_PID); +/* doc device instantiation end */ +/* doc string instantiation start */ USBD_DESC_LANG_DEFINE(sample_lang); USBD_DESC_MANUFACTURER_DEFINE(sample_mfr, CONFIG_SAMPLE_USBD_MANUFACTURER); USBD_DESC_PRODUCT_DEFINE(sample_product, CONFIG_SAMPLE_USBD_PRODUCT); USBD_DESC_SERIAL_NUMBER_DEFINE(sample_sn); +/* doc string instantiation end */ + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); +/* doc configuration instantiation start */ static const uint8_t attributes = (IS_ENABLED(CONFIG_SAMPLE_USBD_SELF_POWERED) ? USB_SCD_SELF_POWERED : 0) | (IS_ENABLED(CONFIG_SAMPLE_USBD_REMOTE_WAKEUP) ? USB_SCD_REMOTE_WAKEUP : 0); +/* Full speed configuration */ USBD_CONFIGURATION_DEFINE(sample_fs_config, attributes, - CONFIG_SAMPLE_USBD_MAX_POWER); + CONFIG_SAMPLE_USBD_MAX_POWER, &fs_cfg_desc); +/* High speed configuration */ USBD_CONFIGURATION_DEFINE(sample_hs_config, attributes, - CONFIG_SAMPLE_USBD_MAX_POWER); + CONFIG_SAMPLE_USBD_MAX_POWER, &hs_cfg_desc); +/* doc configuration instantiation end */ /* * This does not yet provide valuable information, but rather serves as an @@ -51,68 +66,9 @@ static const struct usb_bos_capability_lpm bos_cap_lpm = { USBD_DESC_BOS_DEFINE(sample_usbext, sizeof(bos_cap_lpm), &bos_cap_lpm); -static int register_fs_classes(struct usbd_context *uds_ctx) -{ - int err = 0; - - STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { - /* Pull everything that is enabled in our configuration. */ - err = usbd_register_class(uds_ctx, c_nd->c_data->name, - USBD_SPEED_FS, 1); - if (err) { - LOG_ERR("Failed to register FS %s (%d)", - c_nd->c_data->name, err); - return err; - } - - LOG_DBG("Register FS %s", c_nd->c_data->name); - } - - return err; -} - -static int register_hs_classes(struct usbd_context *uds_ctx) -{ - int err = 0; - - STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { - /* Pull everything that is enabled in our configuration. */ - err = usbd_register_class(uds_ctx, c_nd->c_data->name, - USBD_SPEED_HS, 1); - if (err) { - LOG_ERR("Failed to register HS %s (%d)", - c_nd->c_data->name, err); - return err; - } - - LOG_DBG("Register HS %s", c_nd->c_data->name); - } - - return err; -} - -static int sample_add_configuration(struct usbd_context *uds_ctx, - const enum usbd_speed speed, - struct usbd_config_node *config) +static void sample_fix_code_triple(struct usbd_context *uds_ctx, + const enum usbd_speed speed) { - int err; - - err = usbd_add_configuration(uds_ctx, speed, config); - if (err) { - LOG_ERR("Failed to add configuration (%d)", err); - return err; - } - - if (speed == USBD_SPEED_FS) { - err = register_fs_classes(uds_ctx); - } else if (speed == USBD_SPEED_HS) { - err = register_hs_classes(uds_ctx); - } - - if (err) { - return err; - } - /* Always use class code information from Interface Descriptors */ if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || @@ -127,14 +83,13 @@ static int sample_add_configuration(struct usbd_context *uds_ctx, } else { usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); } - - return 0; } struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) { int err; + /* doc add string descriptor start */ err = usbd_add_descriptor(&sample_usbd, &sample_lang); if (err) { LOG_ERR("Failed to initialize language descriptor (%d)", err); @@ -158,29 +113,52 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) LOG_ERR("Failed to initialize SN descriptor (%d)", err); return NULL; } + /* doc add string descriptor end */ if (usbd_caps_speed(&sample_usbd) == USBD_SPEED_HS) { - err = sample_add_configuration(&sample_usbd, USBD_SPEED_HS, - &sample_hs_config); + err = usbd_add_configuration(&sample_usbd, USBD_SPEED_HS, + &sample_hs_config); if (err) { LOG_ERR("Failed to add High-Speed configuration"); return NULL; } + + err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_HS, 1); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + sample_fix_code_triple(&sample_usbd, USBD_SPEED_HS); } - err = sample_add_configuration(&sample_usbd, USBD_SPEED_FS, - &sample_fs_config); + /* doc configuration register start */ + err = usbd_add_configuration(&sample_usbd, USBD_SPEED_FS, + &sample_fs_config); if (err) { LOG_ERR("Failed to add Full-Speed configuration"); return NULL; } + /* doc configuration register end */ + + /* doc functions register start */ + err = usbd_register_all_classes(&sample_usbd, USBD_SPEED_FS, 1); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + /* doc functions register end */ + + sample_fix_code_triple(&sample_usbd, USBD_SPEED_FS); if (msg_cb != NULL) { + /* doc device init-and-msg start */ err = usbd_msg_register_cb(&sample_usbd, msg_cb); if (err) { LOG_ERR("Failed to register message callback"); return NULL; } + /* doc device init-and-msg end */ } if (IS_ENABLED(CONFIG_SAMPLE_USBD_20_EXTENSION_DESC)) { @@ -194,11 +172,13 @@ struct usbd_context *sample_usbd_init_device(usbd_msg_cb_t msg_cb) } } + /* doc device init start */ err = usbd_init(&sample_usbd); if (err) { LOG_ERR("Failed to initialize device support"); return NULL; } + /* doc device init end */ return &sample_usbd; } diff --git a/samples/subsys/usb/hid-keyboard/src/main.c b/samples/subsys/usb/hid-keyboard/src/main.c index 393edfcf06a..14f360beb3a 100644 --- a/samples/subsys/usb/hid-keyboard/src/main.c +++ b/samples/subsys/usb/hid-keyboard/src/main.c @@ -51,14 +51,16 @@ struct kb_event { K_MSGQ_DEFINE(kb_msgq, sizeof(struct kb_event), 2, 1); -static uint8_t __aligned(sizeof(void *)) report[KB_REPORT_COUNT]; +UDC_STATIC_BUF_DEFINE(report, KB_REPORT_COUNT); static uint32_t kb_duration; static bool kb_ready; -static void input_cb(struct input_event *evt) +static void input_cb(struct input_event *evt, void *user_data) { struct kb_event kb_evt; + ARG_UNUSED(user_data); + kb_evt.code = evt->code; kb_evt.value = evt->value; if (k_msgq_put(&kb_msgq, &kb_evt, K_NO_WAIT) != 0) { @@ -66,7 +68,7 @@ static void input_cb(struct input_event *evt) } } -INPUT_CALLBACK_DEFINE(NULL, input_cb); +INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL); static void kb_iface_ready(const struct device *dev, const bool ready) { @@ -141,6 +143,32 @@ struct hid_device_ops kb_ops = { .output_report = kb_output_report, }; +/* doc device msg-cb start */ +static void msg_cb(struct usbd_context *const usbd_ctx, + const struct usbd_msg *const msg) +{ + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (msg->type == USBD_MSG_CONFIGURATION) { + LOG_INF("\tConfiguration value %d", msg->status); + } + + if (usbd_can_detect_vbus(usbd_ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + if (usbd_enable(usbd_ctx)) { + LOG_ERR("Failed to enable device support"); + } + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + if (usbd_disable(usbd_ctx)) { + LOG_ERR("Failed to disable device support"); + } + } + } +} +/* doc device msg-cb end */ + int main(void) { struct usbd_context *sample_usbd; @@ -178,16 +206,20 @@ int main(void) return ret; } - sample_usbd = sample_usbd_init_device(NULL); + sample_usbd = sample_usbd_init_device(msg_cb); if (sample_usbd == NULL) { LOG_ERR("Failed to initialize USB device"); return -ENODEV; } - ret = usbd_enable(sample_usbd); - if (ret) { - LOG_ERR("Failed to enable device support"); - return ret; + if (!usbd_can_detect_vbus(sample_usbd)) { + /* doc device enable start */ + ret = usbd_enable(sample_usbd); + if (ret) { + LOG_ERR("Failed to enable device support"); + return ret; + } + /* doc device enable end */ } LOG_INF("HID keyboard sample is initialized"); @@ -247,7 +279,7 @@ int main(void) continue; } - ret = hid_device_submit_report(hid_dev, sizeof(report), report); + ret = hid_device_submit_report(hid_dev, KB_REPORT_COUNT, report); if (ret) { LOG_ERR("HID submit report error, %d", ret); } diff --git a/samples/subsys/usb/hid-mouse/prj.conf b/samples/subsys/usb/hid-mouse/prj.conf index 1fbf7592628..b797a7382e8 100644 --- a/samples/subsys/usb/hid-mouse/prj.conf +++ b/samples/subsys/usb/hid-mouse/prj.conf @@ -10,3 +10,4 @@ CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y CONFIG_GPIO=y CONFIG_INPUT=y +CONFIG_INPUT_MODE_SYNCHRONOUS=y diff --git a/samples/subsys/usb/hid-mouse/src/main.c b/samples/subsys/usb/hid-mouse/src/main.c index 28b27877acb..c812a6cbd46 100644 --- a/samples/subsys/usb/hid-mouse/src/main.c +++ b/samples/subsys/usb/hid-mouse/src/main.c @@ -37,8 +37,8 @@ enum mouse_report_idx { MOUSE_REPORT_COUNT = 4, }; -static uint8_t __aligned(sizeof(void *)) report[MOUSE_REPORT_COUNT]; -static K_SEM_DEFINE(report_sem, 0, 1); +K_MSGQ_DEFINE(mouse_msgq, MOUSE_REPORT_COUNT, 2, 1); +static K_SEM_DEFINE(ep_write_sem, 0, 1); static inline void status_cb(enum usb_dc_status_code status, const uint8_t *param) { @@ -55,11 +55,11 @@ static ALWAYS_INLINE void rwup_if_suspended(void) } } -static void input_cb(struct input_event *evt) +static void input_cb(struct input_event *evt, void *user_data) { - uint8_t tmp[MOUSE_REPORT_COUNT]; + static uint8_t tmp[MOUSE_REPORT_COUNT]; - (void)memcpy(tmp, report, sizeof(tmp)); + ARG_UNUSED(user_data); switch (evt->code) { case INPUT_KEY_0: @@ -88,13 +88,16 @@ static void input_cb(struct input_event *evt) return; } - if (memcmp(tmp, report, sizeof(tmp))) { - memcpy(report, tmp, sizeof(report)); - k_sem_give(&report_sem); + if (k_msgq_put(&mouse_msgq, tmp, K_NO_WAIT) != 0) { + LOG_ERR("Failed to put new input event"); } + + tmp[MOUSE_X_REPORT_IDX] = 0U; + tmp[MOUSE_Y_REPORT_IDX] = 0U; + } -INPUT_CALLBACK_DEFINE(NULL, input_cb); +INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL); #if defined(CONFIG_USB_DEVICE_STACK_NEXT) static int enable_usb_device_next(void) @@ -120,6 +123,16 @@ static int enable_usb_device_next(void) } #endif /* IS_ENABLED(CONFIG_USB_DEVICE_STACK_NEXT) */ +static void int_in_ready_cb(const struct device *dev) +{ + ARG_UNUSED(dev); + k_sem_give(&ep_write_sem); +} + +static const struct hid_ops ops = { + .int_in_ready = int_in_ready_cb, +}; + int main(void) { const struct device *hid_dev; @@ -148,7 +161,7 @@ int main(void) usb_hid_register_device(hid_dev, hid_report_desc, sizeof(hid_report_desc), - NULL); + &ops); usb_hid_init(hid_dev); @@ -163,19 +176,17 @@ int main(void) } while (true) { - k_sem_take(&report_sem, K_FOREVER); + UDC_STATIC_BUF_DEFINE(report, MOUSE_REPORT_COUNT); + + k_msgq_get(&mouse_msgq, &report, K_FOREVER); - ret = hid_int_ep_write(hid_dev, report, sizeof(report), NULL); - report[MOUSE_X_REPORT_IDX] = 0U; - report[MOUSE_Y_REPORT_IDX] = 0U; + ret = hid_int_ep_write(hid_dev, report, MOUSE_REPORT_COUNT, NULL); if (ret) { LOG_ERR("HID write error, %d", ret); - } - - /* Toggle LED on sent report */ - ret = gpio_pin_toggle(led0.port, led0.pin); - if (ret < 0) { - LOG_ERR("Failed to toggle the LED pin, error: %d", ret); + } else { + k_sem_take(&ep_write_sem, K_FOREVER); + /* Toggle LED on sent report */ + (void)gpio_pin_toggle(led0.port, led0.pin); } } return 0; diff --git a/samples/subsys/usb/uac2_explicit_feedback/src/main.c b/samples/subsys/usb/uac2_explicit_feedback/src/main.c index a09cb2d5d85..e448416c745 100644 --- a/samples/subsys/usb/uac2_explicit_feedback/src/main.c +++ b/samples/subsys/usb/uac2_explicit_feedback/src/main.c @@ -36,7 +36,8 @@ LOG_MODULE_REGISTER(uac2_sample, LOG_LEVEL_INF); * when USB host decides to perform rapid terminal enable/disable cycles. */ #define I2S_BUFFERS_COUNT 7 -K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, MAX_BLOCK_SIZE, I2S_BUFFERS_COUNT, 4); +K_MEM_SLAB_DEFINE_STATIC(i2s_tx_slab, ROUND_UP(MAX_BLOCK_SIZE, UDC_BUF_GRANULARITY), + I2S_BUFFERS_COUNT, UDC_BUF_ALIGN); struct usb_i2s_ctx { const struct device *i2s_dev; diff --git a/subsys/input/input.c b/subsys/input/input.c index 907788e1853..560f7a02614 100644 --- a/subsys/input/input.c +++ b/subsys/input/input.c @@ -22,7 +22,7 @@ static void input_process(struct input_event *evt) { STRUCT_SECTION_FOREACH(input_callback, callback) { if (callback->dev == NULL || callback->dev == evt->dev) { - callback->callback(evt); + callback->callback(evt, callback->user_data); } } } diff --git a/subsys/input/input_keymap.c b/subsys/input/input_keymap.c index 938d238b2b0..895f1702831 100644 --- a/subsys/input/input_keymap.c +++ b/subsys/input/input_keymap.c @@ -29,8 +29,9 @@ struct keymap_data { bool pressed; }; -static void keymap_cb(const struct device *dev, struct input_event *evt) +static void keymap_cb(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; const struct keymap_config *cfg = dev->config; struct keymap_data *data = dev->data; const uint16_t *codes = cfg->codes; @@ -98,11 +99,8 @@ static int keymap_init(const struct device *dev) KEYMAP_ENTRY_CODE(DT_PROP_BY_IDX(node_id, prop, idx)), #define INPUT_KEYMAP_DEFINE(inst) \ - static void keymap_cb_##inst(struct input_event *evt) \ - { \ - keymap_cb(DEVICE_DT_INST_GET(inst), evt); \ - } \ - INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb_##inst); \ + INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PARENT(inst)), keymap_cb, \ + (void *)DEVICE_DT_INST_GET(inst)); \ \ DT_INST_FOREACH_PROP_ELEM(inst, keymap, KEYMAP_ENTRY_VALIDATE) \ \ diff --git a/subsys/input/input_longpress.c b/subsys/input/input_longpress.c index b7ce3c7c8d6..d53d2ce9be7 100644 --- a/subsys/input/input_longpress.c +++ b/subsys/input/input_longpress.c @@ -46,8 +46,9 @@ static void longpress_deferred(struct k_work *work) entry->long_fired = true; } -static void longpress_cb(const struct device *dev, struct input_event *evt) +static void longpress_cb(struct input_event *evt, void *user_data) { + const struct device *dev = user_data; const struct longpress_config *cfg = dev->config; struct longpress_data_entry *entry; int i; @@ -105,15 +106,11 @@ static int longpress_init(const struct device *dev) #define INPUT_LONGPRESS_DEFINE(inst) \ BUILD_ASSERT((DT_INST_PROP_LEN(inst, input_codes) == \ DT_INST_PROP_LEN_OR(inst, short_codes, 0)) || \ - !DT_INST_NODE_HAS_PROP(inst, short_codes)); \ + !DT_INST_NODE_HAS_PROP(inst, short_codes)); \ BUILD_ASSERT(DT_INST_PROP_LEN(inst, input_codes) == DT_INST_PROP_LEN(inst, long_codes)); \ \ - static void longpress_cb_##inst(struct input_event *evt) \ - { \ - longpress_cb(DEVICE_DT_INST_GET(inst), evt); \ - } \ INPUT_CALLBACK_DEFINE(DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, input)), \ - longpress_cb_##inst); \ + longpress_cb, (void *)DEVICE_DT_INST_GET(inst)); \ \ static const uint16_t longpress_input_codes_##inst[] = DT_INST_PROP(inst, input_codes); \ \ diff --git a/subsys/input/input_utils.c b/subsys/input/input_utils.c index 9e093b27b46..85125b45a8d 100644 --- a/subsys/input/input_utils.c +++ b/subsys/input/input_utils.c @@ -51,8 +51,10 @@ static bool input_dump_enabled(void) } #endif /* CONFIG_INPUT_SHELL */ -static void input_dump_cb(struct input_event *evt) +static void input_dump_cb(struct input_event *evt, void *user_data) { + ARG_UNUSED(user_data); + if (!input_dump_enabled()) { return; } @@ -64,7 +66,7 @@ static void input_dump_cb(struct input_event *evt) evt->code, evt->value); } -INPUT_CALLBACK_DEFINE(NULL, input_dump_cb); +INPUT_CALLBACK_DEFINE(NULL, input_dump_cb, NULL); #endif /* CONFIG_INPUT_EVENT_DUMP */ #ifdef CONFIG_INPUT_SHELL @@ -162,12 +164,14 @@ static void kbd_matrix_state_log_entry(char *header, kbd_row_t *data) kbd_matrix_state_dev->name, header, kbd_matrix_buf, count); } -static void kbd_matrix_state_log(struct input_event *evt) +static void kbd_matrix_state_log(struct input_event *evt, void *user_data) { const struct input_kbd_matrix_common_config *cfg; static uint32_t row, col; static bool val; + ARG_UNUSED(user_data); + if (kbd_matrix_state_dev == NULL || kbd_matrix_state_dev != evt->dev) { return; } @@ -212,7 +216,7 @@ static void kbd_matrix_state_log(struct input_event *evt) kbd_matrix_state_log_entry("state", kbd_matrix_state); } -INPUT_CALLBACK_DEFINE(NULL, kbd_matrix_state_log); +INPUT_CALLBACK_DEFINE(NULL, kbd_matrix_state_log, NULL); static int input_cmd_kbd_matrix_state_dump(const struct shell *sh, size_t argc, char *argv[]) diff --git a/subsys/usb/device/usb_descriptor.c b/subsys/usb/device/usb_descriptor.c index 325989dc7fd..bc933b19820 100644 --- a/subsys/usb/device/usb_descriptor.c +++ b/subsys/usb/device/usb_descriptor.c @@ -242,7 +242,7 @@ static int usb_validate_ep_cfg_data(struct usb_ep_descriptor * const ep_descr, ep_cfg.ep_type = (ep_descr->bmAttributes & USB_EP_TRANSFER_TYPE_MASK); - ep_cfg.ep_mps = ep_descr->wMaxPacketSize; + ep_cfg.ep_mps = sys_le16_to_cpu(ep_descr->wMaxPacketSize); ep_cfg.ep_addr = ep_descr->bEndpointAddress; if (ep_cfg.ep_addr & USB_EP_DIR_IN) { if ((*requested_ep & (1U << (idx + 16U)))) { diff --git a/subsys/usb/device_next/class/bt_hci.c b/subsys/usb/device_next/class/bt_hci.c index fb45713440d..2213fb88bce 100644 --- a/subsys/usb/device_next/class/bt_hci.c +++ b/subsys/usb/device_next/class/bt_hci.c @@ -73,9 +73,9 @@ static K_FIFO_DEFINE(bt_hci_tx_queue); * REVISE: global (bulk, interrupt, iso) specific pools would be more * RAM usage efficient. */ -NET_BUF_POOL_FIXED_DEFINE(bt_hci_ep_pool, - 3, 512, - sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(bt_hci_ep_pool, + 3, 512, + sizeof(struct udc_buf_info), NULL); /* HCI RX/TX threads */ static K_KERNEL_STACK_DEFINE(rx_thread_stack, CONFIG_BT_HCI_TX_STACK_SIZE); static struct k_thread rx_thread_data; @@ -564,7 +564,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_IN, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ @@ -573,7 +573,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_OUT, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ @@ -594,7 +594,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_IN, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = BT_HCI_EP_MPS_VOICE, \ + .wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_MPS_VOICE), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ @@ -603,7 +603,7 @@ static struct usbd_bt_hci_desc bt_hci_desc_##n = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = BT_HCI_EP_VOICE_OUT, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = BT_HCI_EP_MPS_VOICE, \ + .wMaxPacketSize = sys_cpu_to_le16(BT_HCI_EP_MPS_VOICE), \ .bInterval = BT_HCI_EP_INTERVAL_VOICE, \ }, \ \ diff --git a/subsys/usb/device_next/class/loopback.c b/subsys/usb/device_next/class/loopback.c index 8a9688dde53..2237cabcc79 100644 --- a/subsys/usb/device_next/class/loopback.c +++ b/subsys/usb/device_next/class/loopback.c @@ -270,7 +270,7 @@ static struct loopback_desc lb_desc_##x = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x83, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = LB_ISO_EP_INTERVAL, \ }, \ \ @@ -279,7 +279,7 @@ static struct loopback_desc lb_desc_##x = { \ .bDescriptorType = USB_DESC_ENDPOINT, \ .bEndpointAddress = 0x03, \ .bmAttributes = USB_EP_TYPE_ISO, \ - .wMaxPacketSize = 0, \ + .wMaxPacketSize = sys_cpu_to_le16(0), \ .bInterval = LB_ISO_EP_INTERVAL, \ }, \ \ diff --git a/subsys/usb/device_next/class/usbd_cdc_acm.c b/subsys/usb/device_next/class/usbd_cdc_acm.c index 6241914887b..7c2b6504284 100644 --- a/subsys/usb/device_next/class/usbd_cdc_acm.c +++ b/subsys/usb/device_next/class/usbd_cdc_acm.c @@ -32,9 +32,9 @@ #endif LOG_MODULE_REGISTER(usbd_cdc_acm, CONFIG_USBD_CDC_ACM_LOG_LEVEL); -NET_BUF_POOL_FIXED_DEFINE(cdc_acm_ep_pool, - DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, - 512, sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(cdc_acm_ep_pool, + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, + 512, sizeof(struct udc_buf_info), NULL); #define CDC_ACM_DEFAULT_LINECODING {sys_cpu_to_le32(115200), 0, 0, 8} #define CDC_ACM_DEFAULT_INT_EP_MPS 16 diff --git a/subsys/usb/device_next/class/usbd_cdc_ecm.c b/subsys/usb/device_next/class/usbd_cdc_ecm.c index d23c1dd3fe7..3c3072182be 100644 --- a/subsys/usb/device_next/class/usbd_cdc_ecm.c +++ b/subsys/usb/device_next/class/usbd_cdc_ecm.c @@ -36,10 +36,10 @@ enum { * Transfers through two endpoints proceed in a synchronous manner, * with maximum block of NET_ETH_MAX_FRAME_SIZE. */ -NET_BUF_POOL_FIXED_DEFINE(cdc_ecm_ep_pool, - DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, - NET_ETH_MAX_FRAME_SIZE, - sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(cdc_ecm_ep_pool, + DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) * 2, + NET_ETH_MAX_FRAME_SIZE, + sizeof(struct udc_buf_info), NULL); struct cdc_ecm_notification { union { diff --git a/subsys/usb/device_next/class/usbd_hid.c b/subsys/usb/device_next/class/usbd_hid.c index 1b39aa182cf..068a3ff170b 100644 --- a/subsys/usb/device_next/class/usbd_hid.c +++ b/subsys/usb/device_next/class/usbd_hid.c @@ -496,6 +496,8 @@ static struct net_buf *hid_buf_alloc_ext(struct hid_device_data *const ddata, struct net_buf *buf = NULL; struct udc_buf_info *bi; + __ASSERT(IS_UDC_ALIGNED(data), "Application provided unaligned buffer"); + buf = net_buf_alloc_with_data(ddata->pool_in, data, size, K_NO_WAIT); if (!buf) { return NULL; @@ -724,6 +726,8 @@ static const struct hid_device_driver_api hid_device_api = { (USBD_HID_INTERFACE_ALTERNATE_DEFINE(n))) #define USBD_HID_INSTANCE_DEFINE(n) \ + HID_VERIFY_REPORT_SIZES(n); \ + \ NET_BUF_POOL_DEFINE(hid_buf_pool_in_##n, \ CONFIG_USBD_HID_IN_BUF_COUNT, 0, \ sizeof(struct udc_buf_info), NULL); \ diff --git a/subsys/usb/device_next/class/usbd_hid_macros.h b/subsys/usb/device_next/class/usbd_hid_macros.h index c00a18b227a..6a16557bf42 100644 --- a/subsys/usb/device_next/class/usbd_hid_macros.h +++ b/subsys/usb/device_next/class/usbd_hid_macros.h @@ -84,966 +84,6 @@ #define HID_MPS_LESS_65_62 1 #define HID_MPS_LESS_65_63 1 #define HID_MPS_LESS_65_64 1 -#define HID_MPS_LESS_65_65 0 -#define HID_MPS_LESS_65_66 0 -#define HID_MPS_LESS_65_67 0 -#define HID_MPS_LESS_65_68 0 -#define HID_MPS_LESS_65_69 0 -#define HID_MPS_LESS_65_70 0 -#define HID_MPS_LESS_65_71 0 -#define HID_MPS_LESS_65_72 0 -#define HID_MPS_LESS_65_73 0 -#define HID_MPS_LESS_65_74 0 -#define HID_MPS_LESS_65_75 0 -#define HID_MPS_LESS_65_76 0 -#define HID_MPS_LESS_65_77 0 -#define HID_MPS_LESS_65_78 0 -#define HID_MPS_LESS_65_79 0 -#define HID_MPS_LESS_65_80 0 -#define HID_MPS_LESS_65_81 0 -#define HID_MPS_LESS_65_82 0 -#define HID_MPS_LESS_65_83 0 -#define HID_MPS_LESS_65_84 0 -#define HID_MPS_LESS_65_85 0 -#define HID_MPS_LESS_65_86 0 -#define HID_MPS_LESS_65_87 0 -#define HID_MPS_LESS_65_88 0 -#define HID_MPS_LESS_65_89 0 -#define HID_MPS_LESS_65_90 0 -#define HID_MPS_LESS_65_91 0 -#define HID_MPS_LESS_65_92 0 -#define HID_MPS_LESS_65_93 0 -#define HID_MPS_LESS_65_94 0 -#define HID_MPS_LESS_65_95 0 -#define HID_MPS_LESS_65_96 0 -#define HID_MPS_LESS_65_97 0 -#define HID_MPS_LESS_65_98 0 -#define HID_MPS_LESS_65_99 0 -#define HID_MPS_LESS_65_100 0 -#define HID_MPS_LESS_65_101 0 -#define HID_MPS_LESS_65_102 0 -#define HID_MPS_LESS_65_103 0 -#define HID_MPS_LESS_65_104 0 -#define HID_MPS_LESS_65_105 0 -#define HID_MPS_LESS_65_106 0 -#define HID_MPS_LESS_65_107 0 -#define HID_MPS_LESS_65_108 0 -#define HID_MPS_LESS_65_109 0 -#define HID_MPS_LESS_65_110 0 -#define HID_MPS_LESS_65_111 0 -#define HID_MPS_LESS_65_112 0 -#define HID_MPS_LESS_65_113 0 -#define HID_MPS_LESS_65_114 0 -#define HID_MPS_LESS_65_115 0 -#define HID_MPS_LESS_65_116 0 -#define HID_MPS_LESS_65_117 0 -#define HID_MPS_LESS_65_118 0 -#define HID_MPS_LESS_65_119 0 -#define HID_MPS_LESS_65_120 0 -#define HID_MPS_LESS_65_121 0 -#define HID_MPS_LESS_65_122 0 -#define HID_MPS_LESS_65_123 0 -#define HID_MPS_LESS_65_124 0 -#define HID_MPS_LESS_65_125 0 -#define HID_MPS_LESS_65_126 0 -#define HID_MPS_LESS_65_127 0 -#define HID_MPS_LESS_65_128 0 -#define HID_MPS_LESS_65_129 0 -#define HID_MPS_LESS_65_130 0 -#define HID_MPS_LESS_65_131 0 -#define HID_MPS_LESS_65_132 0 -#define HID_MPS_LESS_65_133 0 -#define HID_MPS_LESS_65_134 0 -#define HID_MPS_LESS_65_135 0 -#define HID_MPS_LESS_65_136 0 -#define HID_MPS_LESS_65_137 0 -#define HID_MPS_LESS_65_138 0 -#define HID_MPS_LESS_65_139 0 -#define HID_MPS_LESS_65_140 0 -#define HID_MPS_LESS_65_141 0 -#define HID_MPS_LESS_65_142 0 -#define HID_MPS_LESS_65_143 0 -#define HID_MPS_LESS_65_144 0 -#define HID_MPS_LESS_65_145 0 -#define HID_MPS_LESS_65_146 0 -#define HID_MPS_LESS_65_147 0 -#define HID_MPS_LESS_65_148 0 -#define HID_MPS_LESS_65_149 0 -#define HID_MPS_LESS_65_150 0 -#define HID_MPS_LESS_65_151 0 -#define HID_MPS_LESS_65_152 0 -#define HID_MPS_LESS_65_153 0 -#define HID_MPS_LESS_65_154 0 -#define HID_MPS_LESS_65_155 0 -#define HID_MPS_LESS_65_156 0 -#define HID_MPS_LESS_65_157 0 -#define HID_MPS_LESS_65_158 0 -#define HID_MPS_LESS_65_159 0 -#define HID_MPS_LESS_65_160 0 -#define HID_MPS_LESS_65_161 0 -#define HID_MPS_LESS_65_162 0 -#define HID_MPS_LESS_65_163 0 -#define HID_MPS_LESS_65_164 0 -#define HID_MPS_LESS_65_165 0 -#define HID_MPS_LESS_65_166 0 -#define HID_MPS_LESS_65_167 0 -#define HID_MPS_LESS_65_168 0 -#define HID_MPS_LESS_65_169 0 -#define HID_MPS_LESS_65_170 0 -#define HID_MPS_LESS_65_171 0 -#define HID_MPS_LESS_65_172 0 -#define HID_MPS_LESS_65_173 0 -#define HID_MPS_LESS_65_174 0 -#define HID_MPS_LESS_65_175 0 -#define HID_MPS_LESS_65_176 0 -#define HID_MPS_LESS_65_177 0 -#define HID_MPS_LESS_65_178 0 -#define HID_MPS_LESS_65_179 0 -#define HID_MPS_LESS_65_180 0 -#define HID_MPS_LESS_65_181 0 -#define HID_MPS_LESS_65_182 0 -#define HID_MPS_LESS_65_183 0 -#define HID_MPS_LESS_65_184 0 -#define HID_MPS_LESS_65_185 0 -#define HID_MPS_LESS_65_186 0 -#define HID_MPS_LESS_65_187 0 -#define HID_MPS_LESS_65_188 0 -#define HID_MPS_LESS_65_189 0 -#define HID_MPS_LESS_65_190 0 -#define HID_MPS_LESS_65_191 0 -#define HID_MPS_LESS_65_192 0 -#define HID_MPS_LESS_65_193 0 -#define HID_MPS_LESS_65_194 0 -#define HID_MPS_LESS_65_195 0 -#define HID_MPS_LESS_65_196 0 -#define HID_MPS_LESS_65_197 0 -#define HID_MPS_LESS_65_198 0 -#define HID_MPS_LESS_65_199 0 -#define HID_MPS_LESS_65_200 0 -#define HID_MPS_LESS_65_201 0 -#define HID_MPS_LESS_65_202 0 -#define HID_MPS_LESS_65_203 0 -#define HID_MPS_LESS_65_204 0 -#define HID_MPS_LESS_65_205 0 -#define HID_MPS_LESS_65_206 0 -#define HID_MPS_LESS_65_207 0 -#define HID_MPS_LESS_65_208 0 -#define HID_MPS_LESS_65_209 0 -#define HID_MPS_LESS_65_210 0 -#define HID_MPS_LESS_65_211 0 -#define HID_MPS_LESS_65_212 0 -#define HID_MPS_LESS_65_213 0 -#define HID_MPS_LESS_65_214 0 -#define HID_MPS_LESS_65_215 0 -#define HID_MPS_LESS_65_216 0 -#define HID_MPS_LESS_65_217 0 -#define HID_MPS_LESS_65_218 0 -#define HID_MPS_LESS_65_219 0 -#define HID_MPS_LESS_65_220 0 -#define HID_MPS_LESS_65_221 0 -#define HID_MPS_LESS_65_222 0 -#define HID_MPS_LESS_65_223 0 -#define HID_MPS_LESS_65_224 0 -#define HID_MPS_LESS_65_225 0 -#define HID_MPS_LESS_65_226 0 -#define HID_MPS_LESS_65_227 0 -#define HID_MPS_LESS_65_228 0 -#define HID_MPS_LESS_65_229 0 -#define HID_MPS_LESS_65_230 0 -#define HID_MPS_LESS_65_231 0 -#define HID_MPS_LESS_65_232 0 -#define HID_MPS_LESS_65_233 0 -#define HID_MPS_LESS_65_234 0 -#define HID_MPS_LESS_65_235 0 -#define HID_MPS_LESS_65_236 0 -#define HID_MPS_LESS_65_237 0 -#define HID_MPS_LESS_65_238 0 -#define HID_MPS_LESS_65_239 0 -#define HID_MPS_LESS_65_240 0 -#define HID_MPS_LESS_65_241 0 -#define HID_MPS_LESS_65_242 0 -#define HID_MPS_LESS_65_243 0 -#define HID_MPS_LESS_65_244 0 -#define HID_MPS_LESS_65_245 0 -#define HID_MPS_LESS_65_246 0 -#define HID_MPS_LESS_65_247 0 -#define HID_MPS_LESS_65_248 0 -#define HID_MPS_LESS_65_249 0 -#define HID_MPS_LESS_65_250 0 -#define HID_MPS_LESS_65_251 0 -#define HID_MPS_LESS_65_252 0 -#define HID_MPS_LESS_65_253 0 -#define HID_MPS_LESS_65_254 0 -#define HID_MPS_LESS_65_255 0 -#define HID_MPS_LESS_65_256 0 -#define HID_MPS_LESS_65_257 0 -#define HID_MPS_LESS_65_258 0 -#define HID_MPS_LESS_65_259 0 -#define HID_MPS_LESS_65_260 0 -#define HID_MPS_LESS_65_261 0 -#define HID_MPS_LESS_65_262 0 -#define HID_MPS_LESS_65_263 0 -#define HID_MPS_LESS_65_264 0 -#define HID_MPS_LESS_65_265 0 -#define HID_MPS_LESS_65_266 0 -#define HID_MPS_LESS_65_267 0 -#define HID_MPS_LESS_65_268 0 -#define HID_MPS_LESS_65_269 0 -#define HID_MPS_LESS_65_270 0 -#define HID_MPS_LESS_65_271 0 -#define HID_MPS_LESS_65_272 0 -#define HID_MPS_LESS_65_273 0 -#define HID_MPS_LESS_65_274 0 -#define HID_MPS_LESS_65_275 0 -#define HID_MPS_LESS_65_276 0 -#define HID_MPS_LESS_65_277 0 -#define HID_MPS_LESS_65_278 0 -#define HID_MPS_LESS_65_279 0 -#define HID_MPS_LESS_65_280 0 -#define HID_MPS_LESS_65_281 0 -#define HID_MPS_LESS_65_282 0 -#define HID_MPS_LESS_65_283 0 -#define HID_MPS_LESS_65_284 0 -#define HID_MPS_LESS_65_285 0 -#define HID_MPS_LESS_65_286 0 -#define HID_MPS_LESS_65_287 0 -#define HID_MPS_LESS_65_288 0 -#define HID_MPS_LESS_65_289 0 -#define HID_MPS_LESS_65_290 0 -#define HID_MPS_LESS_65_291 0 -#define HID_MPS_LESS_65_292 0 -#define HID_MPS_LESS_65_293 0 -#define HID_MPS_LESS_65_294 0 -#define HID_MPS_LESS_65_295 0 -#define HID_MPS_LESS_65_296 0 -#define HID_MPS_LESS_65_297 0 -#define HID_MPS_LESS_65_298 0 -#define HID_MPS_LESS_65_299 0 -#define HID_MPS_LESS_65_300 0 -#define HID_MPS_LESS_65_301 0 -#define HID_MPS_LESS_65_302 0 -#define HID_MPS_LESS_65_303 0 -#define HID_MPS_LESS_65_304 0 -#define HID_MPS_LESS_65_305 0 -#define HID_MPS_LESS_65_306 0 -#define HID_MPS_LESS_65_307 0 -#define HID_MPS_LESS_65_308 0 -#define HID_MPS_LESS_65_309 0 -#define HID_MPS_LESS_65_310 0 -#define HID_MPS_LESS_65_311 0 -#define HID_MPS_LESS_65_312 0 -#define HID_MPS_LESS_65_313 0 -#define HID_MPS_LESS_65_314 0 -#define HID_MPS_LESS_65_315 0 -#define HID_MPS_LESS_65_316 0 -#define HID_MPS_LESS_65_317 0 -#define HID_MPS_LESS_65_318 0 -#define HID_MPS_LESS_65_319 0 -#define HID_MPS_LESS_65_320 0 -#define HID_MPS_LESS_65_321 0 -#define HID_MPS_LESS_65_322 0 -#define HID_MPS_LESS_65_323 0 -#define HID_MPS_LESS_65_324 0 -#define HID_MPS_LESS_65_325 0 -#define HID_MPS_LESS_65_326 0 -#define HID_MPS_LESS_65_327 0 -#define HID_MPS_LESS_65_328 0 -#define HID_MPS_LESS_65_329 0 -#define HID_MPS_LESS_65_330 0 -#define HID_MPS_LESS_65_331 0 -#define HID_MPS_LESS_65_332 0 -#define HID_MPS_LESS_65_333 0 -#define HID_MPS_LESS_65_334 0 -#define HID_MPS_LESS_65_335 0 -#define HID_MPS_LESS_65_336 0 -#define HID_MPS_LESS_65_337 0 -#define HID_MPS_LESS_65_338 0 -#define HID_MPS_LESS_65_339 0 -#define HID_MPS_LESS_65_340 0 -#define HID_MPS_LESS_65_341 0 -#define HID_MPS_LESS_65_342 0 -#define HID_MPS_LESS_65_343 0 -#define HID_MPS_LESS_65_344 0 -#define HID_MPS_LESS_65_345 0 -#define HID_MPS_LESS_65_346 0 -#define HID_MPS_LESS_65_347 0 -#define HID_MPS_LESS_65_348 0 -#define HID_MPS_LESS_65_349 0 -#define HID_MPS_LESS_65_350 0 -#define HID_MPS_LESS_65_351 0 -#define HID_MPS_LESS_65_352 0 -#define HID_MPS_LESS_65_353 0 -#define HID_MPS_LESS_65_354 0 -#define HID_MPS_LESS_65_355 0 -#define HID_MPS_LESS_65_356 0 -#define HID_MPS_LESS_65_357 0 -#define HID_MPS_LESS_65_358 0 -#define HID_MPS_LESS_65_359 0 -#define HID_MPS_LESS_65_360 0 -#define HID_MPS_LESS_65_361 0 -#define HID_MPS_LESS_65_362 0 -#define HID_MPS_LESS_65_363 0 -#define HID_MPS_LESS_65_364 0 -#define HID_MPS_LESS_65_365 0 -#define HID_MPS_LESS_65_366 0 -#define HID_MPS_LESS_65_367 0 -#define HID_MPS_LESS_65_368 0 -#define HID_MPS_LESS_65_369 0 -#define HID_MPS_LESS_65_370 0 -#define HID_MPS_LESS_65_371 0 -#define HID_MPS_LESS_65_372 0 -#define HID_MPS_LESS_65_373 0 -#define HID_MPS_LESS_65_374 0 -#define HID_MPS_LESS_65_375 0 -#define HID_MPS_LESS_65_376 0 -#define HID_MPS_LESS_65_377 0 -#define HID_MPS_LESS_65_378 0 -#define HID_MPS_LESS_65_379 0 -#define HID_MPS_LESS_65_380 0 -#define HID_MPS_LESS_65_381 0 -#define HID_MPS_LESS_65_382 0 -#define HID_MPS_LESS_65_383 0 -#define HID_MPS_LESS_65_384 0 -#define HID_MPS_LESS_65_385 0 -#define HID_MPS_LESS_65_386 0 -#define HID_MPS_LESS_65_387 0 -#define HID_MPS_LESS_65_388 0 -#define HID_MPS_LESS_65_389 0 -#define HID_MPS_LESS_65_390 0 -#define HID_MPS_LESS_65_391 0 -#define HID_MPS_LESS_65_392 0 -#define HID_MPS_LESS_65_393 0 -#define HID_MPS_LESS_65_394 0 -#define HID_MPS_LESS_65_395 0 -#define HID_MPS_LESS_65_396 0 -#define HID_MPS_LESS_65_397 0 -#define HID_MPS_LESS_65_398 0 -#define HID_MPS_LESS_65_399 0 -#define HID_MPS_LESS_65_400 0 -#define HID_MPS_LESS_65_401 0 -#define HID_MPS_LESS_65_402 0 -#define HID_MPS_LESS_65_403 0 -#define HID_MPS_LESS_65_404 0 -#define HID_MPS_LESS_65_405 0 -#define HID_MPS_LESS_65_406 0 -#define HID_MPS_LESS_65_407 0 -#define HID_MPS_LESS_65_408 0 -#define HID_MPS_LESS_65_409 0 -#define HID_MPS_LESS_65_410 0 -#define HID_MPS_LESS_65_411 0 -#define HID_MPS_LESS_65_412 0 -#define HID_MPS_LESS_65_413 0 -#define HID_MPS_LESS_65_414 0 -#define HID_MPS_LESS_65_415 0 -#define HID_MPS_LESS_65_416 0 -#define HID_MPS_LESS_65_417 0 -#define HID_MPS_LESS_65_418 0 -#define HID_MPS_LESS_65_419 0 -#define HID_MPS_LESS_65_420 0 -#define HID_MPS_LESS_65_421 0 -#define HID_MPS_LESS_65_422 0 -#define HID_MPS_LESS_65_423 0 -#define HID_MPS_LESS_65_424 0 -#define HID_MPS_LESS_65_425 0 -#define HID_MPS_LESS_65_426 0 -#define HID_MPS_LESS_65_427 0 -#define HID_MPS_LESS_65_428 0 -#define HID_MPS_LESS_65_429 0 -#define HID_MPS_LESS_65_430 0 -#define HID_MPS_LESS_65_431 0 -#define HID_MPS_LESS_65_432 0 -#define HID_MPS_LESS_65_433 0 -#define HID_MPS_LESS_65_434 0 -#define HID_MPS_LESS_65_435 0 -#define HID_MPS_LESS_65_436 0 -#define HID_MPS_LESS_65_437 0 -#define HID_MPS_LESS_65_438 0 -#define HID_MPS_LESS_65_439 0 -#define HID_MPS_LESS_65_440 0 -#define HID_MPS_LESS_65_441 0 -#define HID_MPS_LESS_65_442 0 -#define HID_MPS_LESS_65_443 0 -#define HID_MPS_LESS_65_444 0 -#define HID_MPS_LESS_65_445 0 -#define HID_MPS_LESS_65_446 0 -#define HID_MPS_LESS_65_447 0 -#define HID_MPS_LESS_65_448 0 -#define HID_MPS_LESS_65_449 0 -#define HID_MPS_LESS_65_450 0 -#define HID_MPS_LESS_65_451 0 -#define HID_MPS_LESS_65_452 0 -#define HID_MPS_LESS_65_453 0 -#define HID_MPS_LESS_65_454 0 -#define HID_MPS_LESS_65_455 0 -#define HID_MPS_LESS_65_456 0 -#define HID_MPS_LESS_65_457 0 -#define HID_MPS_LESS_65_458 0 -#define HID_MPS_LESS_65_459 0 -#define HID_MPS_LESS_65_460 0 -#define HID_MPS_LESS_65_461 0 -#define HID_MPS_LESS_65_462 0 -#define HID_MPS_LESS_65_463 0 -#define HID_MPS_LESS_65_464 0 -#define HID_MPS_LESS_65_465 0 -#define HID_MPS_LESS_65_466 0 -#define HID_MPS_LESS_65_467 0 -#define HID_MPS_LESS_65_468 0 -#define HID_MPS_LESS_65_469 0 -#define HID_MPS_LESS_65_470 0 -#define HID_MPS_LESS_65_471 0 -#define HID_MPS_LESS_65_472 0 -#define HID_MPS_LESS_65_473 0 -#define HID_MPS_LESS_65_474 0 -#define HID_MPS_LESS_65_475 0 -#define HID_MPS_LESS_65_476 0 -#define HID_MPS_LESS_65_477 0 -#define HID_MPS_LESS_65_478 0 -#define HID_MPS_LESS_65_479 0 -#define HID_MPS_LESS_65_480 0 -#define HID_MPS_LESS_65_481 0 -#define HID_MPS_LESS_65_482 0 -#define HID_MPS_LESS_65_483 0 -#define HID_MPS_LESS_65_484 0 -#define HID_MPS_LESS_65_485 0 -#define HID_MPS_LESS_65_486 0 -#define HID_MPS_LESS_65_487 0 -#define HID_MPS_LESS_65_488 0 -#define HID_MPS_LESS_65_489 0 -#define HID_MPS_LESS_65_490 0 -#define HID_MPS_LESS_65_491 0 -#define HID_MPS_LESS_65_492 0 -#define HID_MPS_LESS_65_493 0 -#define HID_MPS_LESS_65_494 0 -#define HID_MPS_LESS_65_495 0 -#define HID_MPS_LESS_65_496 0 -#define HID_MPS_LESS_65_497 0 -#define HID_MPS_LESS_65_498 0 -#define HID_MPS_LESS_65_499 0 -#define HID_MPS_LESS_65_500 0 -#define HID_MPS_LESS_65_501 0 -#define HID_MPS_LESS_65_502 0 -#define HID_MPS_LESS_65_503 0 -#define HID_MPS_LESS_65_504 0 -#define HID_MPS_LESS_65_505 0 -#define HID_MPS_LESS_65_506 0 -#define HID_MPS_LESS_65_507 0 -#define HID_MPS_LESS_65_508 0 -#define HID_MPS_LESS_65_509 0 -#define HID_MPS_LESS_65_510 0 -#define HID_MPS_LESS_65_511 0 -#define HID_MPS_LESS_65_512 0 -#define HID_MPS_LESS_65_513 0 -#define HID_MPS_LESS_65_514 0 -#define HID_MPS_LESS_65_515 0 -#define HID_MPS_LESS_65_516 0 -#define HID_MPS_LESS_65_517 0 -#define HID_MPS_LESS_65_518 0 -#define HID_MPS_LESS_65_519 0 -#define HID_MPS_LESS_65_520 0 -#define HID_MPS_LESS_65_521 0 -#define HID_MPS_LESS_65_522 0 -#define HID_MPS_LESS_65_523 0 -#define HID_MPS_LESS_65_524 0 -#define HID_MPS_LESS_65_525 0 -#define HID_MPS_LESS_65_526 0 -#define HID_MPS_LESS_65_527 0 -#define HID_MPS_LESS_65_528 0 -#define HID_MPS_LESS_65_529 0 -#define HID_MPS_LESS_65_530 0 -#define HID_MPS_LESS_65_531 0 -#define HID_MPS_LESS_65_532 0 -#define HID_MPS_LESS_65_533 0 -#define HID_MPS_LESS_65_534 0 -#define HID_MPS_LESS_65_535 0 -#define HID_MPS_LESS_65_536 0 -#define HID_MPS_LESS_65_537 0 -#define HID_MPS_LESS_65_538 0 -#define HID_MPS_LESS_65_539 0 -#define HID_MPS_LESS_65_540 0 -#define HID_MPS_LESS_65_541 0 -#define HID_MPS_LESS_65_542 0 -#define HID_MPS_LESS_65_543 0 -#define HID_MPS_LESS_65_544 0 -#define HID_MPS_LESS_65_545 0 -#define HID_MPS_LESS_65_546 0 -#define HID_MPS_LESS_65_547 0 -#define HID_MPS_LESS_65_548 0 -#define HID_MPS_LESS_65_549 0 -#define HID_MPS_LESS_65_550 0 -#define HID_MPS_LESS_65_551 0 -#define HID_MPS_LESS_65_552 0 -#define HID_MPS_LESS_65_553 0 -#define HID_MPS_LESS_65_554 0 -#define HID_MPS_LESS_65_555 0 -#define HID_MPS_LESS_65_556 0 -#define HID_MPS_LESS_65_557 0 -#define HID_MPS_LESS_65_558 0 -#define HID_MPS_LESS_65_559 0 -#define HID_MPS_LESS_65_560 0 -#define HID_MPS_LESS_65_561 0 -#define HID_MPS_LESS_65_562 0 -#define HID_MPS_LESS_65_563 0 -#define HID_MPS_LESS_65_564 0 -#define HID_MPS_LESS_65_565 0 -#define HID_MPS_LESS_65_566 0 -#define HID_MPS_LESS_65_567 0 -#define HID_MPS_LESS_65_568 0 -#define HID_MPS_LESS_65_569 0 -#define HID_MPS_LESS_65_570 0 -#define HID_MPS_LESS_65_571 0 -#define HID_MPS_LESS_65_572 0 -#define HID_MPS_LESS_65_573 0 -#define HID_MPS_LESS_65_574 0 -#define HID_MPS_LESS_65_575 0 -#define HID_MPS_LESS_65_576 0 -#define HID_MPS_LESS_65_577 0 -#define HID_MPS_LESS_65_578 0 -#define HID_MPS_LESS_65_579 0 -#define HID_MPS_LESS_65_580 0 -#define HID_MPS_LESS_65_581 0 -#define HID_MPS_LESS_65_582 0 -#define HID_MPS_LESS_65_583 0 -#define HID_MPS_LESS_65_584 0 -#define HID_MPS_LESS_65_585 0 -#define HID_MPS_LESS_65_586 0 -#define HID_MPS_LESS_65_587 0 -#define HID_MPS_LESS_65_588 0 -#define HID_MPS_LESS_65_589 0 -#define HID_MPS_LESS_65_590 0 -#define HID_MPS_LESS_65_591 0 -#define HID_MPS_LESS_65_592 0 -#define HID_MPS_LESS_65_593 0 -#define HID_MPS_LESS_65_594 0 -#define HID_MPS_LESS_65_595 0 -#define HID_MPS_LESS_65_596 0 -#define HID_MPS_LESS_65_597 0 -#define HID_MPS_LESS_65_598 0 -#define HID_MPS_LESS_65_599 0 -#define HID_MPS_LESS_65_600 0 -#define HID_MPS_LESS_65_601 0 -#define HID_MPS_LESS_65_602 0 -#define HID_MPS_LESS_65_603 0 -#define HID_MPS_LESS_65_604 0 -#define HID_MPS_LESS_65_605 0 -#define HID_MPS_LESS_65_606 0 -#define HID_MPS_LESS_65_607 0 -#define HID_MPS_LESS_65_608 0 -#define HID_MPS_LESS_65_609 0 -#define HID_MPS_LESS_65_610 0 -#define HID_MPS_LESS_65_611 0 -#define HID_MPS_LESS_65_612 0 -#define HID_MPS_LESS_65_613 0 -#define HID_MPS_LESS_65_614 0 -#define HID_MPS_LESS_65_615 0 -#define HID_MPS_LESS_65_616 0 -#define HID_MPS_LESS_65_617 0 -#define HID_MPS_LESS_65_618 0 -#define HID_MPS_LESS_65_619 0 -#define HID_MPS_LESS_65_620 0 -#define HID_MPS_LESS_65_621 0 -#define HID_MPS_LESS_65_622 0 -#define HID_MPS_LESS_65_623 0 -#define HID_MPS_LESS_65_624 0 -#define HID_MPS_LESS_65_625 0 -#define HID_MPS_LESS_65_626 0 -#define HID_MPS_LESS_65_627 0 -#define HID_MPS_LESS_65_628 0 -#define HID_MPS_LESS_65_629 0 -#define HID_MPS_LESS_65_630 0 -#define HID_MPS_LESS_65_631 0 -#define HID_MPS_LESS_65_632 0 -#define HID_MPS_LESS_65_633 0 -#define HID_MPS_LESS_65_634 0 -#define HID_MPS_LESS_65_635 0 -#define HID_MPS_LESS_65_636 0 -#define HID_MPS_LESS_65_637 0 -#define HID_MPS_LESS_65_638 0 -#define HID_MPS_LESS_65_639 0 -#define HID_MPS_LESS_65_640 0 -#define HID_MPS_LESS_65_641 0 -#define HID_MPS_LESS_65_642 0 -#define HID_MPS_LESS_65_643 0 -#define HID_MPS_LESS_65_644 0 -#define HID_MPS_LESS_65_645 0 -#define HID_MPS_LESS_65_646 0 -#define HID_MPS_LESS_65_647 0 -#define HID_MPS_LESS_65_648 0 -#define HID_MPS_LESS_65_649 0 -#define HID_MPS_LESS_65_650 0 -#define HID_MPS_LESS_65_651 0 -#define HID_MPS_LESS_65_652 0 -#define HID_MPS_LESS_65_653 0 -#define HID_MPS_LESS_65_654 0 -#define HID_MPS_LESS_65_655 0 -#define HID_MPS_LESS_65_656 0 -#define HID_MPS_LESS_65_657 0 -#define HID_MPS_LESS_65_658 0 -#define HID_MPS_LESS_65_659 0 -#define HID_MPS_LESS_65_660 0 -#define HID_MPS_LESS_65_661 0 -#define HID_MPS_LESS_65_662 0 -#define HID_MPS_LESS_65_663 0 -#define HID_MPS_LESS_65_664 0 -#define HID_MPS_LESS_65_665 0 -#define HID_MPS_LESS_65_666 0 -#define HID_MPS_LESS_65_667 0 -#define HID_MPS_LESS_65_668 0 -#define HID_MPS_LESS_65_669 0 -#define HID_MPS_LESS_65_670 0 -#define HID_MPS_LESS_65_671 0 -#define HID_MPS_LESS_65_672 0 -#define HID_MPS_LESS_65_673 0 -#define HID_MPS_LESS_65_674 0 -#define HID_MPS_LESS_65_675 0 -#define HID_MPS_LESS_65_676 0 -#define HID_MPS_LESS_65_677 0 -#define HID_MPS_LESS_65_678 0 -#define HID_MPS_LESS_65_679 0 -#define HID_MPS_LESS_65_680 0 -#define HID_MPS_LESS_65_681 0 -#define HID_MPS_LESS_65_682 0 -#define HID_MPS_LESS_65_683 0 -#define HID_MPS_LESS_65_684 0 -#define HID_MPS_LESS_65_685 0 -#define HID_MPS_LESS_65_686 0 -#define HID_MPS_LESS_65_687 0 -#define HID_MPS_LESS_65_688 0 -#define HID_MPS_LESS_65_689 0 -#define HID_MPS_LESS_65_690 0 -#define HID_MPS_LESS_65_691 0 -#define HID_MPS_LESS_65_692 0 -#define HID_MPS_LESS_65_693 0 -#define HID_MPS_LESS_65_694 0 -#define HID_MPS_LESS_65_695 0 -#define HID_MPS_LESS_65_696 0 -#define HID_MPS_LESS_65_697 0 -#define HID_MPS_LESS_65_698 0 -#define HID_MPS_LESS_65_699 0 -#define HID_MPS_LESS_65_700 0 -#define HID_MPS_LESS_65_701 0 -#define HID_MPS_LESS_65_702 0 -#define HID_MPS_LESS_65_703 0 -#define HID_MPS_LESS_65_704 0 -#define HID_MPS_LESS_65_705 0 -#define HID_MPS_LESS_65_706 0 -#define HID_MPS_LESS_65_707 0 -#define HID_MPS_LESS_65_708 0 -#define HID_MPS_LESS_65_709 0 -#define HID_MPS_LESS_65_710 0 -#define HID_MPS_LESS_65_711 0 -#define HID_MPS_LESS_65_712 0 -#define HID_MPS_LESS_65_713 0 -#define HID_MPS_LESS_65_714 0 -#define HID_MPS_LESS_65_715 0 -#define HID_MPS_LESS_65_716 0 -#define HID_MPS_LESS_65_717 0 -#define HID_MPS_LESS_65_718 0 -#define HID_MPS_LESS_65_719 0 -#define HID_MPS_LESS_65_720 0 -#define HID_MPS_LESS_65_721 0 -#define HID_MPS_LESS_65_722 0 -#define HID_MPS_LESS_65_723 0 -#define HID_MPS_LESS_65_724 0 -#define HID_MPS_LESS_65_725 0 -#define HID_MPS_LESS_65_726 0 -#define HID_MPS_LESS_65_727 0 -#define HID_MPS_LESS_65_728 0 -#define HID_MPS_LESS_65_729 0 -#define HID_MPS_LESS_65_730 0 -#define HID_MPS_LESS_65_731 0 -#define HID_MPS_LESS_65_732 0 -#define HID_MPS_LESS_65_733 0 -#define HID_MPS_LESS_65_734 0 -#define HID_MPS_LESS_65_735 0 -#define HID_MPS_LESS_65_736 0 -#define HID_MPS_LESS_65_737 0 -#define HID_MPS_LESS_65_738 0 -#define HID_MPS_LESS_65_739 0 -#define HID_MPS_LESS_65_740 0 -#define HID_MPS_LESS_65_741 0 -#define HID_MPS_LESS_65_742 0 -#define HID_MPS_LESS_65_743 0 -#define HID_MPS_LESS_65_744 0 -#define HID_MPS_LESS_65_745 0 -#define HID_MPS_LESS_65_746 0 -#define HID_MPS_LESS_65_747 0 -#define HID_MPS_LESS_65_748 0 -#define HID_MPS_LESS_65_749 0 -#define HID_MPS_LESS_65_750 0 -#define HID_MPS_LESS_65_751 0 -#define HID_MPS_LESS_65_752 0 -#define HID_MPS_LESS_65_753 0 -#define HID_MPS_LESS_65_754 0 -#define HID_MPS_LESS_65_755 0 -#define HID_MPS_LESS_65_756 0 -#define HID_MPS_LESS_65_757 0 -#define HID_MPS_LESS_65_758 0 -#define HID_MPS_LESS_65_759 0 -#define HID_MPS_LESS_65_760 0 -#define HID_MPS_LESS_65_761 0 -#define HID_MPS_LESS_65_762 0 -#define HID_MPS_LESS_65_763 0 -#define HID_MPS_LESS_65_764 0 -#define HID_MPS_LESS_65_765 0 -#define HID_MPS_LESS_65_766 0 -#define HID_MPS_LESS_65_767 0 -#define HID_MPS_LESS_65_768 0 -#define HID_MPS_LESS_65_769 0 -#define HID_MPS_LESS_65_770 0 -#define HID_MPS_LESS_65_771 0 -#define HID_MPS_LESS_65_772 0 -#define HID_MPS_LESS_65_773 0 -#define HID_MPS_LESS_65_774 0 -#define HID_MPS_LESS_65_775 0 -#define HID_MPS_LESS_65_776 0 -#define HID_MPS_LESS_65_777 0 -#define HID_MPS_LESS_65_778 0 -#define HID_MPS_LESS_65_779 0 -#define HID_MPS_LESS_65_780 0 -#define HID_MPS_LESS_65_781 0 -#define HID_MPS_LESS_65_782 0 -#define HID_MPS_LESS_65_783 0 -#define HID_MPS_LESS_65_784 0 -#define HID_MPS_LESS_65_785 0 -#define HID_MPS_LESS_65_786 0 -#define HID_MPS_LESS_65_787 0 -#define HID_MPS_LESS_65_788 0 -#define HID_MPS_LESS_65_789 0 -#define HID_MPS_LESS_65_790 0 -#define HID_MPS_LESS_65_791 0 -#define HID_MPS_LESS_65_792 0 -#define HID_MPS_LESS_65_793 0 -#define HID_MPS_LESS_65_794 0 -#define HID_MPS_LESS_65_795 0 -#define HID_MPS_LESS_65_796 0 -#define HID_MPS_LESS_65_797 0 -#define HID_MPS_LESS_65_798 0 -#define HID_MPS_LESS_65_799 0 -#define HID_MPS_LESS_65_800 0 -#define HID_MPS_LESS_65_801 0 -#define HID_MPS_LESS_65_802 0 -#define HID_MPS_LESS_65_803 0 -#define HID_MPS_LESS_65_804 0 -#define HID_MPS_LESS_65_805 0 -#define HID_MPS_LESS_65_806 0 -#define HID_MPS_LESS_65_807 0 -#define HID_MPS_LESS_65_808 0 -#define HID_MPS_LESS_65_809 0 -#define HID_MPS_LESS_65_810 0 -#define HID_MPS_LESS_65_811 0 -#define HID_MPS_LESS_65_812 0 -#define HID_MPS_LESS_65_813 0 -#define HID_MPS_LESS_65_814 0 -#define HID_MPS_LESS_65_815 0 -#define HID_MPS_LESS_65_816 0 -#define HID_MPS_LESS_65_817 0 -#define HID_MPS_LESS_65_818 0 -#define HID_MPS_LESS_65_819 0 -#define HID_MPS_LESS_65_820 0 -#define HID_MPS_LESS_65_821 0 -#define HID_MPS_LESS_65_822 0 -#define HID_MPS_LESS_65_823 0 -#define HID_MPS_LESS_65_824 0 -#define HID_MPS_LESS_65_825 0 -#define HID_MPS_LESS_65_826 0 -#define HID_MPS_LESS_65_827 0 -#define HID_MPS_LESS_65_828 0 -#define HID_MPS_LESS_65_829 0 -#define HID_MPS_LESS_65_830 0 -#define HID_MPS_LESS_65_831 0 -#define HID_MPS_LESS_65_832 0 -#define HID_MPS_LESS_65_833 0 -#define HID_MPS_LESS_65_834 0 -#define HID_MPS_LESS_65_835 0 -#define HID_MPS_LESS_65_836 0 -#define HID_MPS_LESS_65_837 0 -#define HID_MPS_LESS_65_838 0 -#define HID_MPS_LESS_65_839 0 -#define HID_MPS_LESS_65_840 0 -#define HID_MPS_LESS_65_841 0 -#define HID_MPS_LESS_65_842 0 -#define HID_MPS_LESS_65_843 0 -#define HID_MPS_LESS_65_844 0 -#define HID_MPS_LESS_65_845 0 -#define HID_MPS_LESS_65_846 0 -#define HID_MPS_LESS_65_847 0 -#define HID_MPS_LESS_65_848 0 -#define HID_MPS_LESS_65_849 0 -#define HID_MPS_LESS_65_850 0 -#define HID_MPS_LESS_65_851 0 -#define HID_MPS_LESS_65_852 0 -#define HID_MPS_LESS_65_853 0 -#define HID_MPS_LESS_65_854 0 -#define HID_MPS_LESS_65_855 0 -#define HID_MPS_LESS_65_856 0 -#define HID_MPS_LESS_65_857 0 -#define HID_MPS_LESS_65_858 0 -#define HID_MPS_LESS_65_859 0 -#define HID_MPS_LESS_65_860 0 -#define HID_MPS_LESS_65_861 0 -#define HID_MPS_LESS_65_862 0 -#define HID_MPS_LESS_65_863 0 -#define HID_MPS_LESS_65_864 0 -#define HID_MPS_LESS_65_865 0 -#define HID_MPS_LESS_65_866 0 -#define HID_MPS_LESS_65_867 0 -#define HID_MPS_LESS_65_868 0 -#define HID_MPS_LESS_65_869 0 -#define HID_MPS_LESS_65_870 0 -#define HID_MPS_LESS_65_871 0 -#define HID_MPS_LESS_65_872 0 -#define HID_MPS_LESS_65_873 0 -#define HID_MPS_LESS_65_874 0 -#define HID_MPS_LESS_65_875 0 -#define HID_MPS_LESS_65_876 0 -#define HID_MPS_LESS_65_877 0 -#define HID_MPS_LESS_65_878 0 -#define HID_MPS_LESS_65_879 0 -#define HID_MPS_LESS_65_880 0 -#define HID_MPS_LESS_65_881 0 -#define HID_MPS_LESS_65_882 0 -#define HID_MPS_LESS_65_883 0 -#define HID_MPS_LESS_65_884 0 -#define HID_MPS_LESS_65_885 0 -#define HID_MPS_LESS_65_886 0 -#define HID_MPS_LESS_65_887 0 -#define HID_MPS_LESS_65_888 0 -#define HID_MPS_LESS_65_889 0 -#define HID_MPS_LESS_65_890 0 -#define HID_MPS_LESS_65_891 0 -#define HID_MPS_LESS_65_892 0 -#define HID_MPS_LESS_65_893 0 -#define HID_MPS_LESS_65_894 0 -#define HID_MPS_LESS_65_895 0 -#define HID_MPS_LESS_65_896 0 -#define HID_MPS_LESS_65_897 0 -#define HID_MPS_LESS_65_898 0 -#define HID_MPS_LESS_65_899 0 -#define HID_MPS_LESS_65_900 0 -#define HID_MPS_LESS_65_901 0 -#define HID_MPS_LESS_65_902 0 -#define HID_MPS_LESS_65_903 0 -#define HID_MPS_LESS_65_904 0 -#define HID_MPS_LESS_65_905 0 -#define HID_MPS_LESS_65_906 0 -#define HID_MPS_LESS_65_907 0 -#define HID_MPS_LESS_65_908 0 -#define HID_MPS_LESS_65_909 0 -#define HID_MPS_LESS_65_910 0 -#define HID_MPS_LESS_65_911 0 -#define HID_MPS_LESS_65_912 0 -#define HID_MPS_LESS_65_913 0 -#define HID_MPS_LESS_65_914 0 -#define HID_MPS_LESS_65_915 0 -#define HID_MPS_LESS_65_916 0 -#define HID_MPS_LESS_65_917 0 -#define HID_MPS_LESS_65_918 0 -#define HID_MPS_LESS_65_919 0 -#define HID_MPS_LESS_65_920 0 -#define HID_MPS_LESS_65_921 0 -#define HID_MPS_LESS_65_922 0 -#define HID_MPS_LESS_65_923 0 -#define HID_MPS_LESS_65_924 0 -#define HID_MPS_LESS_65_925 0 -#define HID_MPS_LESS_65_926 0 -#define HID_MPS_LESS_65_927 0 -#define HID_MPS_LESS_65_928 0 -#define HID_MPS_LESS_65_929 0 -#define HID_MPS_LESS_65_930 0 -#define HID_MPS_LESS_65_931 0 -#define HID_MPS_LESS_65_932 0 -#define HID_MPS_LESS_65_933 0 -#define HID_MPS_LESS_65_934 0 -#define HID_MPS_LESS_65_935 0 -#define HID_MPS_LESS_65_936 0 -#define HID_MPS_LESS_65_937 0 -#define HID_MPS_LESS_65_938 0 -#define HID_MPS_LESS_65_939 0 -#define HID_MPS_LESS_65_940 0 -#define HID_MPS_LESS_65_941 0 -#define HID_MPS_LESS_65_942 0 -#define HID_MPS_LESS_65_943 0 -#define HID_MPS_LESS_65_944 0 -#define HID_MPS_LESS_65_945 0 -#define HID_MPS_LESS_65_946 0 -#define HID_MPS_LESS_65_947 0 -#define HID_MPS_LESS_65_948 0 -#define HID_MPS_LESS_65_949 0 -#define HID_MPS_LESS_65_950 0 -#define HID_MPS_LESS_65_951 0 -#define HID_MPS_LESS_65_952 0 -#define HID_MPS_LESS_65_953 0 -#define HID_MPS_LESS_65_954 0 -#define HID_MPS_LESS_65_955 0 -#define HID_MPS_LESS_65_956 0 -#define HID_MPS_LESS_65_957 0 -#define HID_MPS_LESS_65_958 0 -#define HID_MPS_LESS_65_959 0 -#define HID_MPS_LESS_65_960 0 -#define HID_MPS_LESS_65_961 0 -#define HID_MPS_LESS_65_962 0 -#define HID_MPS_LESS_65_963 0 -#define HID_MPS_LESS_65_964 0 -#define HID_MPS_LESS_65_965 0 -#define HID_MPS_LESS_65_966 0 -#define HID_MPS_LESS_65_967 0 -#define HID_MPS_LESS_65_968 0 -#define HID_MPS_LESS_65_969 0 -#define HID_MPS_LESS_65_970 0 -#define HID_MPS_LESS_65_971 0 -#define HID_MPS_LESS_65_972 0 -#define HID_MPS_LESS_65_973 0 -#define HID_MPS_LESS_65_974 0 -#define HID_MPS_LESS_65_975 0 -#define HID_MPS_LESS_65_976 0 -#define HID_MPS_LESS_65_977 0 -#define HID_MPS_LESS_65_978 0 -#define HID_MPS_LESS_65_979 0 -#define HID_MPS_LESS_65_980 0 -#define HID_MPS_LESS_65_981 0 -#define HID_MPS_LESS_65_982 0 -#define HID_MPS_LESS_65_983 0 -#define HID_MPS_LESS_65_984 0 -#define HID_MPS_LESS_65_985 0 -#define HID_MPS_LESS_65_986 0 -#define HID_MPS_LESS_65_987 0 -#define HID_MPS_LESS_65_988 0 -#define HID_MPS_LESS_65_989 0 -#define HID_MPS_LESS_65_990 0 -#define HID_MPS_LESS_65_991 0 -#define HID_MPS_LESS_65_992 0 -#define HID_MPS_LESS_65_993 0 -#define HID_MPS_LESS_65_994 0 -#define HID_MPS_LESS_65_995 0 -#define HID_MPS_LESS_65_996 0 -#define HID_MPS_LESS_65_997 0 -#define HID_MPS_LESS_65_998 0 -#define HID_MPS_LESS_65_999 0 -#define HID_MPS_LESS_65_1000 0 -#define HID_MPS_LESS_65_1001 0 -#define HID_MPS_LESS_65_1002 0 -#define HID_MPS_LESS_65_1003 0 -#define HID_MPS_LESS_65_1004 0 -#define HID_MPS_LESS_65_1005 0 -#define HID_MPS_LESS_65_1006 0 -#define HID_MPS_LESS_65_1007 0 -#define HID_MPS_LESS_65_1008 0 -#define HID_MPS_LESS_65_1009 0 -#define HID_MPS_LESS_65_1010 0 -#define HID_MPS_LESS_65_1011 0 -#define HID_MPS_LESS_65_1012 0 -#define HID_MPS_LESS_65_1013 0 -#define HID_MPS_LESS_65_1014 0 -#define HID_MPS_LESS_65_1015 0 -#define HID_MPS_LESS_65_1016 0 -#define HID_MPS_LESS_65_1017 0 -#define HID_MPS_LESS_65_1018 0 -#define HID_MPS_LESS_65_1019 0 -#define HID_MPS_LESS_65_1020 0 -#define HID_MPS_LESS_65_1021 0 -#define HID_MPS_LESS_65_1022 0 -#define HID_MPS_LESS_65_1023 0 -#define HID_MPS_LESS_65_1024 0 #define HID_MPS_LESS_65(x) UTIL_PRIMITIVE_CAT(HID_MPS_LESS_65_, x) @@ -1052,8 +92,8 @@ * configure an alternate interface. */ #define HID_ALL_MPS_LESS_65(n) \ - UTIL_AND(HID_MPS_LESS_65(DT_INST_PROP(n, out_report_size)), \ - HID_MPS_LESS_65(DT_INST_PROP(n, in_report_size))) + COND_CODE_1(HID_MPS_LESS_65(DT_INST_PROP_OR(n, out_report_size, 0)), \ + (HID_MPS_LESS_65(DT_INST_PROP(n, in_report_size))), (0)) /* Get IN endpoint polling rate based on the desired speed. */ #define HID_IN_EP_INTERVAL(n, hs) \ @@ -1113,7 +153,7 @@ */ #define HID_OUT_EP_MPS(n, alt) \ COND_CODE_1(alt, \ - (sys_cpu_to_le16(DT_INST_PROP(n, out_report_size))), \ + (sys_cpu_to_le16(USB_TPL_TO_MPS(DT_INST_PROP(n, out_report_size)))), \ (sys_cpu_to_le16(MIN(DT_INST_PROP(n, out_report_size), 64U)))) /* @@ -1122,7 +162,7 @@ */ #define HID_IN_EP_MPS(n, alt) \ COND_CODE_1(alt, \ - (sys_cpu_to_le16(DT_INST_PROP(n, in_report_size))), \ + (sys_cpu_to_le16(USB_TPL_TO_MPS(DT_INST_PROP(n, in_report_size)))), \ (sys_cpu_to_le16(MIN(DT_INST_PROP(n, in_report_size), 64U)))) #define HID_OUT_EP_DEFINE(n, hs, alt) \ @@ -1156,7 +196,7 @@ #define HID_OUT_POOL_DEFINE(n) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, out_report_size), \ - (NET_BUF_POOL_DEFINE(hid_buf_pool_out_##n, \ + (UDC_BUF_POOL_DEFINE(hid_buf_pool_out_##n, \ CONFIG_USBD_HID_OUT_BUF_COUNT, \ DT_INST_PROP(n, out_report_size), \ sizeof(struct udc_buf_info), NULL)), \ @@ -1166,4 +206,10 @@ COND_CODE_1(DT_INST_NODE_HAS_PROP(n, out_report_size), \ (&hid_buf_pool_out_##n), (NULL)) +#define HID_VERIFY_REPORT_SIZES(n) \ + BUILD_ASSERT(USB_TPL_IS_VALID(DT_INST_PROP_OR(n, out_report_size, 0)), \ + "out-report-size must be valid Total Packet Length"); \ + BUILD_ASSERT(USB_TPL_IS_VALID(DT_INST_PROP_OR(n, in_report_size, 0)), \ + "in-report-size must be valid Total Packet Length"); + #endif /* ZEPHYR_USB_DEVICE_CLASS_HID_MACROS_H_ */ diff --git a/subsys/usb/device_next/class/usbd_msc.c b/subsys/usb/device_next/class/usbd_msc.c index a608a2da61a..b3a7771fa9f 100644 --- a/subsys/usb/device_next/class/usbd_msc.c +++ b/subsys/usb/device_next/class/usbd_msc.c @@ -63,9 +63,9 @@ struct CSW { /* Can be 64 if device is not High-Speed capable */ #define MSC_BUF_SIZE 512 -NET_BUF_POOL_FIXED_DEFINE(msc_ep_pool, - MSC_NUM_INSTANCES * 2, MSC_BUF_SIZE, - sizeof(struct udc_buf_info), NULL); +UDC_BUF_POOL_DEFINE(msc_ep_pool, + MSC_NUM_INSTANCES * 2, MSC_BUF_SIZE, + sizeof(struct udc_buf_info), NULL); struct msc_event { struct usbd_class_data *c_data; diff --git a/subsys/usb/device_next/class/usbd_uac2.c b/subsys/usb/device_next/class/usbd_uac2.c index 3229be15f49..5e861689a84 100644 --- a/subsys/usb/device_next/class/usbd_uac2.c +++ b/subsys/usb/device_next/class/usbd_uac2.c @@ -40,7 +40,7 @@ LOG_MODULE_REGISTER(usbd_uac2, CONFIG_USBD_UAC2_LOG_LEVEL); * "wasted memory" here is likely to be smaller than the memory overhead for * more complex "only as much as needed" schemes (e.g. heap). */ -NET_BUF_POOL_DEFINE(uac2_pool, UAC2_NUM_ENDPOINTS, 6, +UDC_BUF_POOL_DEFINE(uac2_pool, UAC2_NUM_ENDPOINTS, 6, sizeof(struct udc_buf_info), NULL); /* 5.2.2 Control Request Layout */ @@ -204,6 +204,8 @@ uac2_buf_alloc(const uint8_t ep, void *data, uint16_t size) struct net_buf *buf = NULL; struct udc_buf_info *bi; + __ASSERT(IS_UDC_ALIGNED(data), "Application provided unaligned buffer"); + buf = net_buf_alloc_with_data(&uac2_pool, data, size, K_NO_WAIT); if (!buf) { return NULL; diff --git a/subsys/usb/device_next/class/usbd_uac2_macros.h b/subsys/usb/device_next/class/usbd_uac2_macros.h index 58716214b56..7182e8e34b6 100644 --- a/subsys/usb/device_next/class/usbd_uac2_macros.h +++ b/subsys/usb/device_next/class/usbd_uac2_macros.h @@ -464,7 +464,7 @@ USB_DESC_ENDPOINT, /* bDescriptorType */ \ FIRST_IN_EP_ADDR, /* bEndpointAddress */ \ USB_EP_TYPE_INTERRUPT, /* bmAttributes */ \ - 0x06, /* wMaxPacketSize */ \ + U16_LE(0x06), /* wMaxPacketSize */ \ 0x01, /* bInterval */ \ #define AC_ENDPOINT_DESCRIPTOR_ARRAY(node) \ diff --git a/subsys/usb/device_next/usbd_ch9.c b/subsys/usb/device_next/usbd_ch9.c index c9b05073314..1de59c1cc39 100644 --- a/subsys/usb/device_next/usbd_ch9.c +++ b/subsys/usb/device_next/usbd_ch9.c @@ -19,6 +19,7 @@ #include "usbd_class.h" #include "usbd_class_api.h" #include "usbd_interface.h" +#include "usbd_msg.h" #include LOG_MODULE_REGISTER(usbd_ch9, CONFIG_USBD_LOG_LEVEL); @@ -171,6 +172,10 @@ static int sreq_set_configuration(struct usbd_context *const uds_ctx) uds_ctx->ch9_data.state = USBD_STATE_CONFIGURED; } + if (ret == 0) { + usbd_msg_pub_simple(uds_ctx, USBD_MSG_CONFIGURATION, setup->wValue); + } + return ret; } diff --git a/subsys/usb/device_next/usbd_class.c b/subsys/usb/device_next/usbd_class.c index 265bffbe947..465480bd0e3 100644 --- a/subsys/usb/device_next/usbd_class.c +++ b/subsys/usb/device_next/usbd_class.c @@ -340,6 +340,42 @@ int usbd_register_class(struct usbd_context *const uds_ctx, return ret; } +int usbd_register_all_classes(struct usbd_context *const uds_ctx, + const enum usbd_speed speed, const uint8_t cfg) +{ + int ret; + + if (speed == USBD_SPEED_HS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { + ret = usbd_register_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to register %s to HS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + if (speed == USBD_SPEED_FS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { + ret = usbd_register_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to register %s to FS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + return -ENOTSUP; +} + int usbd_unregister_class(struct usbd_context *const uds_ctx, const char *name, const enum usbd_speed speed, const uint8_t cfg) @@ -407,3 +443,39 @@ int usbd_unregister_class(struct usbd_context *const uds_ctx, usbd_device_unlock(uds_ctx); return ret; } + +int usbd_unregister_all_classes(struct usbd_context *const uds_ctx, + const enum usbd_speed speed, const uint8_t cfg) +{ + int ret; + + if (speed == USBD_SPEED_HS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_hs, usbd_class_node, c_nd) { + ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to unregister %s to HS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + if (speed == USBD_SPEED_FS) { + STRUCT_SECTION_FOREACH_ALTERNATE(usbd_class_fs, usbd_class_node, c_nd) { + ret = usbd_unregister_class(uds_ctx, c_nd->c_data->name, + speed, cfg); + if (ret) { + LOG_ERR("Failed to unregister %s to FS configuration %u", + c_nd->c_data->name, cfg); + return ret; + } + } + + return 0; + } + + return -ENOTSUP; +} diff --git a/subsys/usb/device_next/usbd_config.c b/subsys/usb/device_next/usbd_config.c index 25a2a082bc7..9d8627028a9 100644 --- a/subsys/usb/device_next/usbd_config.c +++ b/subsys/usb/device_next/usbd_config.c @@ -319,9 +319,17 @@ int usbd_add_configuration(struct usbd_context *const uds_ctx, usbd_set_num_configs(uds_ctx, speed, num); } - sys_slist_append(configs, &cfg_nd->node); + if (cfg_nd->str_desc_nd != NULL) { + ret = usbd_add_descriptor(uds_ctx, cfg_nd->str_desc_nd); + if (ret != 0) { + LOG_ERR("Failed to add configuration string descriptor"); + goto add_configuration_exit; + } - usbd_device_unlock(uds_ctx); + desc->iConfiguration = usbd_str_desc_get_idx(cfg_nd->str_desc_nd); + } + + sys_slist_append(configs, &cfg_nd->node); add_configuration_exit: usbd_device_unlock(uds_ctx); diff --git a/subsys/usb/device_next/usbd_desc.c b/subsys/usb/device_next/usbd_desc.c index 158cdb1ba22..469953d1d27 100644 --- a/subsys/usb/device_next/usbd_desc.c +++ b/subsys/usb/device_next/usbd_desc.c @@ -109,7 +109,8 @@ int usbd_desc_remove_all(struct usbd_context *const uds_ctx) while ((node = sys_dlist_get(&uds_ctx->descriptors))) { tmp = CONTAINER_OF(node, struct usbd_desc_node, node); - LOG_DBG("Remove descriptor node %p", tmp); + LOG_DBG("Remove descriptor node %p type %u", + (void *)tmp, tmp->str.utype); } return 0; diff --git a/subsys/usb/device_next/usbd_endpoint.c b/subsys/usb/device_next/usbd_endpoint.c index 5d49e0df8ea..1d41f4c99a2 100644 --- a/subsys/usb/device_next/usbd_endpoint.c +++ b/subsys/usb/device_next/usbd_endpoint.c @@ -24,7 +24,7 @@ int usbd_ep_enable(const struct device *dev, int ret; ret = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), ed->bInterval); if (ret == 0) { usbd_ep_bm_set(ep_bm, ed->bEndpointAddress); } diff --git a/subsys/usb/device_next/usbd_init.c b/subsys/usb/device_next/usbd_init.c index 69de723b221..551dc32164f 100644 --- a/subsys/usb/device_next/usbd_init.c +++ b/subsys/usb/device_next/usbd_init.c @@ -28,7 +28,7 @@ static int assign_ep_addr(const struct device *dev, int ret = -ENODEV; for (unsigned int idx = 1; idx < 16U; idx++) { - uint16_t mps = ed->wMaxPacketSize; + uint16_t mps = sys_le16_to_cpu(ed->wMaxPacketSize); uint8_t ep; if (USB_EP_DIR_IS_IN(ed->bEndpointAddress)) { @@ -50,7 +50,7 @@ static int assign_ep_addr(const struct device *dev, if (ret == 0) { LOG_DBG("ep 0x%02x -> 0x%02x", ed->bEndpointAddress, ep); ed->bEndpointAddress = ep; - ed->wMaxPacketSize = mps; + ed->wMaxPacketSize = sys_cpu_to_le16(mps); usbd_ep_bm_set(class_ep_bm, ed->bEndpointAddress); usbd_ep_bm_set(config_ep_bm, ed->bEndpointAddress); @@ -166,8 +166,10 @@ static int init_configuration_inst(struct usbd_context *const uds_ctx, return ret; } - LOG_INF("\tep 0x%02x mps %u interface ep-bm 0x%08x", - ed->bEndpointAddress, ed->wMaxPacketSize, class_ep_bm); + LOG_INF("\tep 0x%02x mps 0x%04x interface ep-bm 0x%08x", + ed->bEndpointAddress, + sys_le16_to_cpu(ed->wMaxPacketSize), + class_ep_bm); } dhp++; diff --git a/subsys/usb/device_next/usbd_shell.c b/subsys/usb/device_next/usbd_shell.c index 173a2a07627..82815306777 100644 --- a/subsys/usb/device_next/usbd_shell.c +++ b/subsys/usb/device_next/usbd_shell.c @@ -15,10 +15,10 @@ #include /* Default configurations used in the shell context. */ -USBD_CONFIGURATION_DEFINE(config_1_fs, USB_SCD_REMOTE_WAKEUP, 200); -USBD_CONFIGURATION_DEFINE(config_1_hs, USB_SCD_REMOTE_WAKEUP, 200); -USBD_CONFIGURATION_DEFINE(config_2_fs, USB_SCD_SELF_POWERED, 200); -USBD_CONFIGURATION_DEFINE(config_2_hs, USB_SCD_SELF_POWERED, 200); +USBD_CONFIGURATION_DEFINE(config_1_fs, USB_SCD_REMOTE_WAKEUP, 200, NULL); +USBD_CONFIGURATION_DEFINE(config_1_hs, USB_SCD_REMOTE_WAKEUP, 200, NULL); +USBD_CONFIGURATION_DEFINE(config_2_fs, USB_SCD_SELF_POWERED, 200, NULL); +USBD_CONFIGURATION_DEFINE(config_2_hs, USB_SCD_SELF_POWERED, 200, NULL); static struct usbd_shell_config { struct usbd_config_node *cfg_nd; diff --git a/tests/drivers/input/gpio_kbd_matrix/src/main.c b/tests/drivers/input/gpio_kbd_matrix/src/main.c index 9600b510f0a..c7e0e933a4a 100644 --- a/tests/drivers/input/gpio_kbd_matrix/src/main.c +++ b/tests/drivers/input/gpio_kbd_matrix/src/main.c @@ -195,7 +195,7 @@ static int last_checked_event_count; zassert_equal(_val, test_event_data.val); \ } -static void test_cb(struct input_event *evt) +static void test_cb(struct input_event *evt, void *user_data) { static int row, col, val; @@ -220,7 +220,7 @@ static void test_cb(struct input_event *evt) test_event_data.event_count, row, col, val); } } -INPUT_CALLBACK_DEFINE(NULL, test_cb); +INPUT_CALLBACK_DEFINE(NULL, test_cb, NULL); /* actual tests */ diff --git a/tests/drivers/input/gpio_keys/src/main.c b/tests/drivers/input/gpio_keys/src/main.c index b5764d7283a..a8f12a8be6b 100644 --- a/tests/drivers/input/gpio_keys/src/main.c +++ b/tests/drivers/input/gpio_keys/src/main.c @@ -35,7 +35,7 @@ ZTEST_SUITE(gpio_keys, NULL, NULL, NULL, NULL, NULL); static int event_count; static uint16_t last_code; static bool last_val; -static void test_gpio_keys_cb_handler(struct input_event *evt) +static void test_gpio_keys_cb_handler(struct input_event *evt, void *user_data) { TC_PRINT("GPIO_KEY %s pressed, zephyr_code=%u, value=%d\n", evt->dev->name, evt->code, evt->value); @@ -43,7 +43,7 @@ static void test_gpio_keys_cb_handler(struct input_event *evt) last_code = evt->code; last_val = evt->value; } -INPUT_CALLBACK_DEFINE(test_gpio_keys_dev, test_gpio_keys_cb_handler); +INPUT_CALLBACK_DEFINE(test_gpio_keys_dev, test_gpio_keys_cb_handler, NULL); /** * @brief TestPurpose: Verify gpio_keys_config pressed raw. diff --git a/tests/drivers/input/kbd_matrix/src/main.c b/tests/drivers/input/kbd_matrix/src/main.c index bd09d4441fc..8477a6cc50b 100644 --- a/tests/drivers/input/kbd_matrix/src/main.c +++ b/tests/drivers/input/kbd_matrix/src/main.c @@ -103,7 +103,7 @@ static int last_checked_event_count; zassert_equal(_val, test_event_data.val); \ } -static void test_cb(struct input_event *evt) +static void test_cb(struct input_event *evt, void *user_data) { static int row, col, val; @@ -128,7 +128,7 @@ static void test_cb(struct input_event *evt) test_event_data.event_count, row, col, val); } } -INPUT_CALLBACK_DEFINE(test_dev, test_cb); +INPUT_CALLBACK_DEFINE(test_dev, test_cb, NULL); #define WAIT_FOR_IDLE_TIMEOUT_US (5 * USEC_PER_SEC) diff --git a/tests/drivers/udc/src/main.c b/tests/drivers/udc/src/main.c index 24e1eb4d1f2..6ca5d8a2548 100644 --- a/tests/drivers/udc/src/main.c +++ b/tests/drivers/udc/src/main.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -91,7 +92,7 @@ static void test_udc_thread(void *p1, void *p2, void *p3) static void test_udc_ep_try_config(const struct device *dev, struct usb_ep_descriptor *ed) { - uint16_t mps = ed->wMaxPacketSize; + uint16_t mps = sys_le16_to_cpu(ed->wMaxPacketSize); int err; err = udc_ep_try_config(dev, ed->bEndpointAddress, @@ -125,13 +126,17 @@ static void test_udc_ep_enable(const struct device *dev, int err1, err2, err3, err4; err1 = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); err2 = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); err3 = udc_ep_enable(dev, FALSE_EP_ADDR, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); err4 = udc_ep_enable(dev, ctrl_ep, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); if (!udc_is_initialized(dev) && !udc_is_enabled(dev)) { zassert_equal(err1, -EPERM, "Not failed to enable endpoint"); @@ -188,7 +193,7 @@ static struct net_buf *test_udc_ep_buf_alloc(const struct device *dev, struct net_buf *buf; buf = udc_ep_buf_alloc(dev, ed->bEndpointAddress, - ed->wMaxPacketSize); + USB_MPS_TO_TPL(sys_le16_to_cpu(ed->wMaxPacketSize))); zassert_not_null(buf, "Failed to allocate request"); @@ -328,13 +333,14 @@ static void test_udc_ep_api(const struct device *dev, for (int i = 0; i < num_of_iterations; i++) { err = udc_ep_enable(dev, ed->bEndpointAddress, ed->bmAttributes, - ed->wMaxPacketSize, ed->bInterval); + sys_le16_to_cpu(ed->wMaxPacketSize), + ed->bInterval); zassert_ok(err, "Failed to enable endpoint"); /* It needs a little reserve for memory management overhead. */ for (int n = 0; n < (CONFIG_UDC_BUF_COUNT - 2); n++) { buf = udc_ep_buf_alloc(dev, ed->bEndpointAddress, - ed->wMaxPacketSize); + USB_MPS_TO_TPL(sys_le16_to_cpu(ed->wMaxPacketSize))); zassert_not_null(buf, "Failed to allocate request (%d) for 0x%02x", n, ed->bEndpointAddress); @@ -363,7 +369,7 @@ static void test_udc_ep_mps(uint8_t type) .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = type, - .wMaxPacketSize = 0, + .wMaxPacketSize = sys_cpu_to_le16(0), .bInterval = 0, }; const struct device *dev; @@ -400,7 +406,7 @@ static void test_udc_ep_mps(uint8_t type) continue; } - ed.wMaxPacketSize = mps[i]; + ed.wMaxPacketSize = sys_cpu_to_le16(mps[i]); test_udc_ep_api(dev, &ed); ed.bEndpointAddress |= USB_EP_DIR_IN; @@ -441,7 +447,7 @@ static struct usb_ep_descriptor ed_ctrl_out = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = USB_CONTROL_EP_OUT, .bmAttributes = USB_EP_TYPE_CONTROL, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; @@ -450,7 +456,7 @@ static struct usb_ep_descriptor ed_ctrl_in = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = USB_CONTROL_EP_IN, .bmAttributes = USB_EP_TYPE_CONTROL, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; @@ -459,7 +465,7 @@ static struct usb_ep_descriptor ed_bulk_out = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x01, .bmAttributes = USB_EP_TYPE_BULK, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; @@ -468,7 +474,7 @@ static struct usb_ep_descriptor ed_bulk_in = { .bDescriptorType = USB_DESC_ENDPOINT, .bEndpointAddress = 0x81, .bmAttributes = USB_EP_TYPE_BULK, - .wMaxPacketSize = 64, + .wMaxPacketSize = sys_cpu_to_le16(64), .bInterval = 0, }; diff --git a/tests/subsys/input/api/src/main.c b/tests/subsys/input/api/src/main.c index c61e3c58551..0dffadedbd3 100644 --- a/tests/subsys/input/api/src/main.c +++ b/tests/subsys/input/api/src/main.c @@ -17,7 +17,7 @@ static int message_count_unfiltered; static K_SEM_DEFINE(cb_start, 1, 1); static K_SEM_DEFINE(cb_done, 1, 1); -static void input_cb_filtered(struct input_event *evt) +static void input_cb_filtered(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d\n", __func__, message_count_filtered); @@ -29,9 +29,9 @@ static void input_cb_filtered(struct input_event *evt) k_sem_give(&cb_start); } -INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered); +INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered, NULL); -static void input_cb_unfiltered(struct input_event *evt) +static void input_cb_unfiltered(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d\n", __func__, message_count_unfiltered); @@ -42,7 +42,7 @@ static void input_cb_unfiltered(struct input_event *evt) k_sem_give(&cb_done); } } -INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered); +INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered, NULL); ZTEST(input_api, test_sequence_thread) { @@ -85,19 +85,19 @@ ZTEST(input_api, test_sequence_thread) #else /* CONFIG_INPUT_MODE_THREAD */ -static void input_cb_filtered(struct input_event *evt) +static void input_cb_filtered(struct input_event *evt, void *user_data) { if (evt->dev == &fake_dev) { message_count_filtered++; } } -INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered); +INPUT_CALLBACK_DEFINE(&fake_dev, input_cb_filtered, NULL); -static void input_cb_unfiltered(struct input_event *evt) +static void input_cb_unfiltered(struct input_event *evt, void *user_data) { message_count_unfiltered++; } -INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered); +INPUT_CALLBACK_DEFINE(NULL, input_cb_unfiltered, NULL); ZTEST(input_api, test_synchronous) { @@ -118,11 +118,11 @@ ZTEST(input_api, test_synchronous) static struct input_event last_event; -static void input_cb_last_event(struct input_event *evt) +static void input_cb_last_event(struct input_event *evt, void *user_data) { memcpy(&last_event, evt, sizeof(last_event)); } -INPUT_CALLBACK_DEFINE(NULL, input_cb_last_event); +INPUT_CALLBACK_DEFINE(NULL, input_cb_last_event, NULL); ZTEST(input_api, test_report_apis) { diff --git a/tests/subsys/input/longpress/src/main.c b/tests/subsys/input/longpress/src/main.c index 4f81901a742..83ea8979a8b 100644 --- a/tests/subsys/input/longpress/src/main.c +++ b/tests/subsys/input/longpress/src/main.c @@ -22,7 +22,7 @@ DEVICE_DT_DEFINE(DT_INST(0, vnd_input_device), NULL, NULL, NULL, NULL, static int event_count; static struct input_event last_events[2]; -static void test_cb(struct input_event *evt) +static void test_cb(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d %x %d\n", __func__, event_count, evt->code, evt->value); @@ -30,11 +30,11 @@ static void test_cb(struct input_event *evt) memcpy(&last_events[1], &last_events[0], sizeof(struct input_event)); memcpy(&last_events[0], evt, sizeof(struct input_event)); } -INPUT_CALLBACK_DEFINE(longpress_dev, test_cb); +INPUT_CALLBACK_DEFINE(longpress_dev, test_cb, NULL); static int event_count_no_short; static struct input_event last_events_no_short[2]; -static void test_cb_no_short(struct input_event *evt) +static void test_cb_no_short(struct input_event *evt, void *user_data) { TC_PRINT("%s: %d %x %d\n", __func__, event_count_no_short, evt->code, evt->value); @@ -42,7 +42,7 @@ static void test_cb_no_short(struct input_event *evt) memcpy(&last_events_no_short[1], &last_events_no_short[0], sizeof(struct input_event)); memcpy(&last_events_no_short[0], evt, sizeof(struct input_event)); } -INPUT_CALLBACK_DEFINE(longpress_no_short_dev, test_cb_no_short); +INPUT_CALLBACK_DEFINE(longpress_no_short_dev, test_cb_no_short, NULL); ZTEST(longpress, test_longpress_test) { diff --git a/tests/subsys/usb/device_next/src/main.c b/tests/subsys/usb/device_next/src/main.c index 5d6a9da7039..a0fcb0e8396 100644 --- a/tests/subsys/usb/device_next/src/main.c +++ b/tests/subsys/usb/device_next/src/main.c @@ -19,11 +19,11 @@ LOG_MODULE_REGISTER(usb_test, LOG_LEVEL_INF); USBD_CONFIGURATION_DEFINE(test_fs_config, USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP, - 200); + 200, NULL); USBD_CONFIGURATION_DEFINE(test_hs_config, USB_SCD_SELF_POWERED | USB_SCD_REMOTE_WAKEUP, - 200); + 200, NULL); USBD_DESC_LANG_DEFINE(test_lang); @@ -134,10 +134,22 @@ static void *usb_test_enable(void) zassert_equal(err, 0, "Failed to add configuration (%d)"); if (usbd_caps_speed(&test_usbd) == USBD_SPEED_HS) { + err = usbd_register_all_classes(&test_usbd, USBD_SPEED_HS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + + err = usbd_unregister_all_classes(&test_usbd, USBD_SPEED_HS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_HS, 1); zassert_equal(err, 0, "Failed to register loopback_0 class (%d)"); } + err = usbd_register_all_classes(&test_usbd, USBD_SPEED_FS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + + err = usbd_unregister_all_classes(&test_usbd, USBD_SPEED_FS, 1); + zassert_equal(err, 0, "Failed to unregister all instances(%d)"); + err = usbd_register_class(&test_usbd, "loopback_0", USBD_SPEED_FS, 1); zassert_equal(err, 0, "Failed to register loopback_0 class (%d)");