linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/8] Add MA USB Host driver
@ 2020-03-12 14:42 Vladimir Stankovic
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-03-12 14:42 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

Media Agnostic Universal Serial Bus (MA USB) Host driver provides USB
connectivity over an available network, allowing host device to access
remote USB devices attached to one or more MA USB devices (accessible
via network).

This driver has been developed to enable the host to communicate
with DisplayLink products supporting MA USB protocol (MA USB device,
in terms of MA USB Specification).

MA USB protocol used by MA USB Host driver has been implemented in
accordance with MA USB Specification Release 1.0b.

This driver depends on the functions provided by DisplayLink's
user-space driver.

v2:
- Fixed licensing info in headers
- Reorganized code to lower file count
- Patch has been split into 8 smaller patches

v3:
- Fixed nested spinlock usage
- Implemented IPv6 support

Vladimir Stankovic (8):
   usb: Add MA-USB Host kernel module
   usb: mausb_host: Add link layer implementation
   usb: mausb_host: HCD initialization
   usb: mausb_host: Implement initial hub handlers
   usb: mausb_host: Introduce PAL processing
   usb: mausb_host: Add logic for PAL-to-PAL communication
   usb: mausb_host: MA-USB PAL events processing
   usb: mausb_host: Process MA-USB data packets

  MAINTAINERS                                  |    7 +
  drivers/usb/Kconfig                          |    2 +
  drivers/usb/Makefile                         |    2 +
  drivers/usb/mausb_host/Kconfig               |   14 +
  drivers/usb/mausb_host/Makefile              |   17 +
  drivers/usb/mausb_host/hcd.c                 | 1897 ++++++++++++++++
  drivers/usb/mausb_host/hcd.h                 |  162 ++
  drivers/usb/mausb_host/hpal.c                | 2083 ++++++++++++++++++
  drivers/usb/mausb_host/hpal.h                |  339 +++
  drivers/usb/mausb_host/hpal_data.c           |  719 ++++++
  drivers/usb/mausb_host/hpal_data.h           |   34 +
  drivers/usb/mausb_host/hpal_events.c         |  611 +++++
  drivers/usb/mausb_host/hpal_events.h         |   85 +
  drivers/usb/mausb_host/ip_link.c             |  374 ++++
  drivers/usb/mausb_host/ip_link.h             |   87 +
  drivers/usb/mausb_host/ma_usb.h              |  869 ++++++++
  drivers/usb/mausb_host/mausb_address.h       |   26 +
  drivers/usb/mausb_host/mausb_core.c          |  212 ++
  drivers/usb/mausb_host/mausb_driver_status.h |   17 +
  drivers/usb/mausb_host/mausb_event.h         |  224 ++
  drivers/usb/mausb_host/utils.c               |  360 +++
  drivers/usb/mausb_host/utils.h               |   45 +
  22 files changed, 8186 insertions(+)
  create mode 100644 drivers/usb/mausb_host/Kconfig
  create mode 100644 drivers/usb/mausb_host/Makefile
  create mode 100644 drivers/usb/mausb_host/hcd.c
  create mode 100644 drivers/usb/mausb_host/hcd.h
  create mode 100644 drivers/usb/mausb_host/hpal.c
  create mode 100644 drivers/usb/mausb_host/hpal.h
  create mode 100644 drivers/usb/mausb_host/hpal_data.c
  create mode 100644 drivers/usb/mausb_host/hpal_data.h
  create mode 100644 drivers/usb/mausb_host/hpal_events.c
  create mode 100644 drivers/usb/mausb_host/hpal_events.h
  create mode 100644 drivers/usb/mausb_host/ip_link.c
  create mode 100644 drivers/usb/mausb_host/ip_link.h
  create mode 100644 drivers/usb/mausb_host/ma_usb.h
  create mode 100644 drivers/usb/mausb_host/mausb_address.h
  create mode 100644 drivers/usb/mausb_host/mausb_core.c
  create mode 100644 drivers/usb/mausb_host/mausb_driver_status.h
  create mode 100644 drivers/usb/mausb_host/mausb_event.h
  create mode 100644 drivers/usb/mausb_host/utils.c
  create mode 100644 drivers/usb/mausb_host/utils.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v4 0/8] Add MA USB Host driver
  2020-03-12 14:42 [PATCH v3 0/8] Add MA USB Host driver Vladimir Stankovic
@ 2020-03-27 15:26 ` vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
                     ` (8 more replies)
  0 siblings, 9 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Media Agnostic Universal Serial Bus (MA USB) Host driver provides USB
connectivity over an available network, allowing host device to access
remote USB devices attached to one or more MA USB devices (accessible
via network).

This driver has been developed to enable the host to communicate
with DisplayLink products supporting MA USB protocol (MA USB device,
in terms of MA USB Specification).

MA USB protocol used by MA USB Host driver has been implemented in
accordance with MA USB Specification Release 1.0b.

This driver depends on the functions provided by DisplayLink's
user-space driver.

v2:
- Fixed licensing info in headers
- Reorganized code to lower file count
- Patch has been split into 8 smaller patches

v3:
- Fixed nested spinlock usage
- Implemented IPv6 support

v4:
- No code changes
- Re-sent patch with different mail client config

Vladimir Stankovic (8):
  usb: Add MA-USB Host kernel module
  usb: mausb_host: Add link layer implementation
  usb: mausb_host: HCD initialization
  usb: mausb_host: Implement initial hub handlers
  usb: mausb_host: Introduce PAL processing
  usb: mausb_host: Add logic for PAL-to-PAL communication
  usb: mausb_host: MA-USB PAL events processing
  usb: mausb_host: Process MA-USB data packets

 MAINTAINERS                                  |    7 +
 drivers/usb/Kconfig                          |    2 +
 drivers/usb/Makefile                         |    2 +
 drivers/usb/mausb_host/Kconfig               |   14 +
 drivers/usb/mausb_host/Makefile              |   17 +
 drivers/usb/mausb_host/hcd.c                 | 1897 ++++++++++++++++
 drivers/usb/mausb_host/hcd.h                 |  162 ++
 drivers/usb/mausb_host/hpal.c                | 2083 ++++++++++++++++++
 drivers/usb/mausb_host/hpal.h                |  339 +++
 drivers/usb/mausb_host/hpal_data.c           |  719 ++++++
 drivers/usb/mausb_host/hpal_data.h           |   34 +
 drivers/usb/mausb_host/hpal_events.c         |  611 +++++
 drivers/usb/mausb_host/hpal_events.h         |   85 +
 drivers/usb/mausb_host/ip_link.c             |  374 ++++
 drivers/usb/mausb_host/ip_link.h             |   87 +
 drivers/usb/mausb_host/ma_usb.h              |  869 ++++++++
 drivers/usb/mausb_host/mausb_address.h       |   26 +
 drivers/usb/mausb_host/mausb_core.c          |  212 ++
 drivers/usb/mausb_host/mausb_driver_status.h |   17 +
 drivers/usb/mausb_host/mausb_event.h         |  224 ++
 drivers/usb/mausb_host/utils.c               |  360 +++
 drivers/usb/mausb_host/utils.h               |   45 +
 22 files changed, 8186 insertions(+)
 create mode 100644 drivers/usb/mausb_host/Kconfig
 create mode 100644 drivers/usb/mausb_host/Makefile
 create mode 100644 drivers/usb/mausb_host/hcd.c
 create mode 100644 drivers/usb/mausb_host/hcd.h
 create mode 100644 drivers/usb/mausb_host/hpal.c
 create mode 100644 drivers/usb/mausb_host/hpal.h
 create mode 100644 drivers/usb/mausb_host/hpal_data.c
 create mode 100644 drivers/usb/mausb_host/hpal_data.h
 create mode 100644 drivers/usb/mausb_host/hpal_events.c
 create mode 100644 drivers/usb/mausb_host/hpal_events.h
 create mode 100644 drivers/usb/mausb_host/ip_link.c
 create mode 100644 drivers/usb/mausb_host/ip_link.h
 create mode 100644 drivers/usb/mausb_host/ma_usb.h
 create mode 100644 drivers/usb/mausb_host/mausb_address.h
 create mode 100644 drivers/usb/mausb_host/mausb_core.c
 create mode 100644 drivers/usb/mausb_host/mausb_driver_status.h
 create mode 100644 drivers/usb/mausb_host/mausb_event.h
 create mode 100644 drivers/usb/mausb_host/utils.c
 create mode 100644 drivers/usb/mausb_host/utils.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v4 1/8] usb: Add MA-USB Host kernel module
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-27 16:25     ` Alan Stern
  2020-03-27 15:26   ` [PATCH v4 2/8] usb: mausb_host: Add link layer implementation vladimir.stankovic
                     ` (7 subsequent siblings)
  8 siblings, 1 reply; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Added utility macros, kernel device creation and cleanup, functions for
handling log formatting and a placeholder module for MA-USB Host device
driver.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 MAINTAINERS                         |  7 +++
 drivers/usb/Kconfig                 |  2 +
 drivers/usb/Makefile                |  2 +
 drivers/usb/mausb_host/Kconfig      | 14 +++++
 drivers/usb/mausb_host/Makefile     | 12 ++++
 drivers/usb/mausb_host/mausb_core.c | 90 +++++++++++++++++++++++++++++
 drivers/usb/mausb_host/utils.c      | 85 +++++++++++++++++++++++++++
 drivers/usb/mausb_host/utils.h      | 40 +++++++++++++
 8 files changed, 252 insertions(+)
 create mode 100644 drivers/usb/mausb_host/Kconfig
 create mode 100644 drivers/usb/mausb_host/Makefile
 create mode 100644 drivers/usb/mausb_host/mausb_core.c
 create mode 100644 drivers/usb/mausb_host/utils.c
 create mode 100644 drivers/usb/mausb_host/utils.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 235ab38ed478..12aac44196d7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10226,6 +10226,13 @@ W:	https://linuxtv.org
 S:	Maintained
 F:	drivers/media/radio/radio-maxiradio*
 
+MA USB HOST DRIVER
+M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
+L:	mausb-host-devel@displaylink.com
+W:	https://www.displaylink.com
+S:	Maintained
+F:	drivers/usb/mausb_host/*
+
 MCAN MMIO DEVICE DRIVER
 M:	Dan Murphy <dmurphy@ti.com>
 M:	Sriram Dash <sriram.dash@samsung.com>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 275568abc670..4e92f1fa0fa5 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -164,6 +164,8 @@ source "drivers/usb/misc/Kconfig"
 
 source "drivers/usb/atm/Kconfig"
 
+source "drivers/usb/mausb_host/Kconfig"
+
 endif # USB
 
 source "drivers/usb/phy/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 1c1c1d659394..22d1998db0e2 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -66,3 +66,5 @@ obj-$(CONFIG_USBIP_CORE)	+= usbip/
 obj-$(CONFIG_TYPEC)		+= typec/
 
 obj-$(CONFIG_USB_ROLE_SWITCH)	+= roles/
+
+obj-$(CONFIG_HOST_MAUSB)        += mausb_host/
diff --git a/drivers/usb/mausb_host/Kconfig b/drivers/usb/mausb_host/Kconfig
new file mode 100644
index 000000000000..2465aac713fe
--- /dev/null
+++ b/drivers/usb/mausb_host/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel configuration file for MA-USB Host driver.
+#
+# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+#
+
+config HOST_MAUSB
+	tristate "MA-USB Host Driver"
+	depends on USB=y
+	default n
+	help
+		This is a MA-USB Host driver which enables host communication
+		via MA-USB protocol stack.
diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
new file mode 100644
index 000000000000..2e353fa0958b
--- /dev/null
+++ b/drivers/usb/mausb_host/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DisplayLink MA-USB Host driver.
+#
+# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+#
+
+obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
+mausb_host-y := mausb_core.o
+mausb_host-y += utils.o
+
+ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
new file mode 100644
index 000000000000..8638dd0a4856
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/net.h>
+
+#include "utils.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("DisplayLink (UK) Ltd.");
+MODULE_VERSION(MAUSB_DRIVER_VERSION);
+
+static int mausb_client_connect(const char *value,
+				const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mausb_client_disconnect(const char *value,
+				   const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mausb_device_connect(const char *value,
+				const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mausb_device_disconnect(const char *value,
+				   const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static const struct kernel_param_ops mausb_device_connect_ops = {
+	.set = mausb_device_connect
+};
+
+static const struct kernel_param_ops mausb_device_disconnect_ops = {
+	.set = mausb_device_disconnect
+};
+
+static const struct kernel_param_ops mausb_client_connect_ops = {
+	.set = mausb_client_connect
+};
+
+static const struct kernel_param_ops mausb_client_disconnect_ops = {
+	.set = mausb_client_disconnect
+};
+
+static int mausb_host_init(void)
+{
+	int status = mausb_create_dev();
+
+	mausb_pr_info("Module load. Version=%s", MAUSB_DRIVER_VERSION);
+
+	if (status < 0) {
+		mausb_pr_alert("Failed to load MAUSB module!");
+		return status;
+	}
+
+	return 0;
+}
+
+static void mausb_host_exit(void)
+{
+	mausb_pr_info("Module unloading started...");
+	mausb_cleanup_dev(1);
+	mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION);
+}
+
+module_init(mausb_host_init);
+module_exit(mausb_host_exit);
diff --git a/drivers/usb/mausb_host/utils.c b/drivers/usb/mausb_host/utils.c
new file mode 100644
index 000000000000..c055b578e402
--- /dev/null
+++ b/drivers/usb/mausb_host/utils.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "utils.h"
+
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#define MAUSB_KERNEL_DEV_NAME "mausb_host"
+#define MAUSB_READ_DEVICE_TIMEOUT_MS 500
+
+static dev_t mausb_major_kernel;
+static struct cdev  mausb_kernel_dev;
+static struct class *mausb_kernel_class;
+
+static int mausb_file_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static int mausb_file_close(struct inode *inode, struct file *filp)
+{
+	kfree(filp->private_data);
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static const struct file_operations mausb_file_ops = {
+	.open	 = mausb_file_open,
+	.release = mausb_file_close,
+};
+
+int mausb_create_dev(void)
+{
+	int device_created = 0;
+	int status = alloc_chrdev_region(&mausb_major_kernel, 0, 1,
+					 MAUSB_KERNEL_DEV_NAME "_proc");
+	if (status)
+		goto cleanup;
+
+	mausb_kernel_class = class_create(THIS_MODULE,
+					  MAUSB_KERNEL_DEV_NAME "_sys");
+	if (!mausb_kernel_class) {
+		status = -ENOMEM;
+		goto cleanup;
+	}
+
+	if (!device_create(mausb_kernel_class, NULL, mausb_major_kernel, NULL,
+			   MAUSB_KERNEL_DEV_NAME "_dev")) {
+		status = -ENOMEM;
+		goto cleanup;
+	}
+	device_created = 1;
+	cdev_init(&mausb_kernel_dev, &mausb_file_ops);
+	status = cdev_add(&mausb_kernel_dev, mausb_major_kernel, 1);
+	if (status)
+		goto cleanup;
+	return 0;
+cleanup:
+	mausb_cleanup_dev(device_created);
+	return status;
+}
+
+void mausb_cleanup_dev(int device_created)
+{
+	if (device_created) {
+		device_destroy(mausb_kernel_class, mausb_major_kernel);
+		cdev_del(&mausb_kernel_dev);
+	}
+
+	if (mausb_kernel_class)
+		class_destroy(mausb_kernel_class);
+
+	unregister_chrdev_region(mausb_major_kernel, 1);
+}
diff --git a/drivers/usb/mausb_host/utils.h b/drivers/usb/mausb_host/utils.h
new file mode 100644
index 000000000000..699f94fcb75b
--- /dev/null
+++ b/drivers/usb/mausb_host/utils.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_UTILS_H__
+#define __MAUSB_UTILS_H__
+
+#if defined(MAUSB_NO_LOGS)
+#define mausb_pr_logs(...)
+#else
+#include <linux/printk.h>
+#include <linux/sched.h>
+#define mausb_pr_logs(level_str, level, log, ...)\
+	pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
+	current->pid, current->tgid, __func__, ##__VA_ARGS__)
+#endif /* MAUSB_NO_LOGS */
+
+#define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
+
+#define mausb_pr_err(...) mausb_pr_logs(err, 2, ##__VA_ARGS__)
+
+#define mausb_pr_warn(...) mausb_pr_logs(warn, 3, ##__VA_ARGS__)
+
+#define mausb_pr_info(...) mausb_pr_logs(info, 4, ##__VA_ARGS__)
+
+#if defined(MAUSB_LOG_VERBOSE)
+	#define mausb_pr_debug(...) mausb_pr_logs(debug, 5, ##__VA_ARGS__)
+#else
+	#define mausb_pr_debug(...)
+#endif /* defined(MAUSB_LOG_VERBOSE) */
+
+#define MAUSB_STRINGIFY2(x) #x
+#define MAUSB_STRINGIFY(x) MAUSB_STRINGIFY2(x)
+
+#define MAUSB_DRIVER_VERSION MAUSB_STRINGIFY(1.2.0.0.36451aec)
+
+int mausb_create_dev(void);
+void mausb_cleanup_dev(int device_created);
+
+#endif /* __MAUSB_UTILS_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 2/8] usb: mausb_host: Add link layer implementation
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
                     ` (6 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented link layer using kernel sockets. Link layer
manages network communication and provides interface for upper
layers of MA-USB stack.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile  |   1 +
 drivers/usb/mausb_host/ip_link.c | 374 +++++++++++++++++++++++++++++++
 drivers/usb/mausb_host/ip_link.h |  87 +++++++
 3 files changed, 462 insertions(+)
 create mode 100644 drivers/usb/mausb_host/ip_link.c
 create mode 100644 drivers/usb/mausb_host/ip_link.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index 2e353fa0958b..19445b73b50b 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -8,5 +8,6 @@
 obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
 mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
+mausb_host-y += ip_link.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/ip_link.c b/drivers/usb/mausb_host/ip_link.c
new file mode 100644
index 000000000000..49b592c02210
--- /dev/null
+++ b/drivers/usb/mausb_host/ip_link.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "ip_link.h"
+
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/workqueue.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+
+#include "utils.h"
+
+static void __mausb_ip_connect(struct work_struct *work);
+static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx);
+static void __mausb_ip_recv_work(struct work_struct *work);
+static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx
+					     *recv_ctx);
+static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx
+					    *recv_ctx);
+
+int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx,
+		      struct net *net_ns,
+		      char ip_addr[INET6_ADDRSTRLEN],
+		      u16 port, void *context,
+		      void (*fn_callback)(void *ctx, enum mausb_channel channel,
+					  enum mausb_link_action act,
+					  int status, void *data),
+		      enum mausb_channel channel)
+{
+	struct mausb_ip_ctx *ctx;
+
+	*ip_ctx = kzalloc(sizeof(**ip_ctx), GFP_ATOMIC);
+	if (!*ip_ctx)
+		return -ENOMEM;
+
+	ctx = *ip_ctx;
+	ctx->client_socket = NULL;
+	__mausb_ip_recv_ctx_clear(&ctx->recv_ctx);
+
+	if (in4_pton(ip_addr, -1,
+		     (u8 *)&ctx->dev_addr_in.sa_in.sin_addr.s_addr, -1,
+		     NULL) == 1) {
+		ctx->dev_addr_in.sa_in.sin_family = AF_INET;
+		ctx->dev_addr_in.sa_in.sin_port = htons(port);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (in6_pton(ip_addr, -1,
+			    (u8 *)&ctx->dev_addr_in.sa_in6.sin6_addr.in6_u, -1,
+			    NULL) == 1) {
+		ctx->dev_addr_in.sa_in6.sin6_family = AF_INET6;
+		ctx->dev_addr_in.sa_in6.sin6_port = htons(port);
+#endif
+	} else {
+		mausb_pr_err("Invalid IP address received: address=%s",
+			     ip_addr);
+		kfree(ctx);
+		return -EINVAL;
+	}
+
+	ctx->net_ns = net_ns;
+
+	if (channel == MAUSB_ISOCH_CHANNEL)
+		ctx->udp = true;
+
+	ctx->connect_workq = alloc_ordered_workqueue("connect_workq",
+						     WQ_MEM_RECLAIM);
+	if (!ctx->connect_workq) {
+		kfree(ctx);
+		return -ENOMEM;
+	}
+
+	ctx->recv_workq = alloc_ordered_workqueue("recv_workq", WQ_MEM_RECLAIM);
+	if (!ctx->recv_workq) {
+		destroy_workqueue(ctx->connect_workq);
+		kfree(ctx);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&ctx->connect_work, __mausb_ip_connect);
+	INIT_WORK(&ctx->recv_work, __mausb_ip_recv_work);
+
+	ctx->channel	 = channel;
+	ctx->ctx	 = context;
+	ctx->fn_callback = fn_callback;
+
+	return 0;
+}
+
+void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx)
+{
+	if (!ip_ctx)
+		return;
+
+	if (ip_ctx->connect_workq) {
+		flush_workqueue(ip_ctx->connect_workq);
+		destroy_workqueue(ip_ctx->connect_workq);
+	}
+
+	if (ip_ctx->recv_workq) {
+		flush_workqueue(ip_ctx->recv_workq);
+		destroy_workqueue(ip_ctx->recv_workq);
+	}
+	if (ip_ctx->client_socket)
+		sock_release(ip_ctx->client_socket);
+	__mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx);
+
+	kfree(ip_ctx);
+}
+
+static void __mausb_ip_set_options(struct socket *sock, bool udp)
+{
+	u32 optval = 0;
+	unsigned int optlen = sizeof(optval);
+	int status = 0;
+	struct __kernel_sock_timeval timeo = {.tv_sec = 0, .tv_usec = 500000U };
+	struct __kernel_sock_timeval send_timeo = {.tv_sec = 1, .tv_usec = 0 };
+
+	if (!udp) {
+		optval = 1;
+		status = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+					   (char *)&optval, optlen);
+		if (status < 0)
+			mausb_pr_warn("Failed to set tcp no delay option: status=%d",
+				      status);
+	}
+
+	status = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_NEW,
+				   (char *)&timeo, sizeof(timeo));
+	if (status < 0)
+		mausb_pr_warn("Failed to set recv timeout option: status=%d",
+			      status);
+
+	status = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_NEW,
+				   (char *)&send_timeo, sizeof(send_timeo));
+	if (status < 0)
+		mausb_pr_warn("Failed to set snd timeout option: status=%d",
+			      status);
+
+	optval = MAUSB_LINK_BUFF_SIZE;
+	status  = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		mausb_pr_warn("Failed to set recv buffer size: status=%d",
+			      status);
+
+	optval = MAUSB_LINK_BUFF_SIZE;
+	status  = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		mausb_pr_warn("Failed to set send buffer size: status=%d",
+			      status);
+
+	optval = MAUSB_LINK_TOS_LEVEL_EF;
+	status  = kernel_setsockopt(sock, IPPROTO_IP, IP_TOS,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		mausb_pr_warn("Failed to set QOS: status=%d", status);
+}
+
+static void __mausb_ip_connect(struct work_struct *work)
+{
+	int status = 0;
+	struct sockaddr *sa;
+	int sa_size;
+	struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx,
+						   connect_work);
+	unsigned short int family = ip_ctx->dev_addr_in.sa_in.sin_family;
+
+	if (!ip_ctx->udp) {
+		status = sock_create_kern(ip_ctx->net_ns, family, SOCK_STREAM,
+					  IPPROTO_TCP, &ip_ctx->client_socket);
+		if (status < 0) {
+			mausb_pr_err("Failed to create socket: status=%d",
+				     status);
+			goto callback;
+		}
+	} else {
+		status = sock_create_kern(ip_ctx->net_ns, family, SOCK_DGRAM,
+					  IPPROTO_UDP, &ip_ctx->client_socket);
+		if (status < 0) {
+			mausb_pr_err("Failed to create socket: status=%d",
+				     status);
+			goto callback;
+		}
+	}
+
+	__mausb_ip_set_options((struct socket *)ip_ctx->client_socket,
+			       ip_ctx->udp);
+
+	if (family == AF_INET) {
+		sa = (struct sockaddr *)&ip_ctx->dev_addr_in.sa_in;
+		sa_size = sizeof(ip_ctx->dev_addr_in.sa_in);
+		mausb_pr_info("Connecting to %pI4:%d, status=%d",
+			      &ip_ctx->dev_addr_in.sa_in.sin_addr,
+			      htons(ip_ctx->dev_addr_in.sa_in.sin_port),
+			      status);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (family == AF_INET6) {
+		sa = (struct sockaddr *)&ip_ctx->dev_addr_in.sa_in6;
+		sa_size = sizeof(ip_ctx->dev_addr_in.sa_in6);
+		mausb_pr_info("Connecting to %pI6c:%d, status=%d",
+			      &ip_ctx->dev_addr_in.sa_in6.sin6_addr,
+			      htons(ip_ctx->dev_addr_in.sa_in6.sin6_port),
+			      status);
+#endif
+	} else {
+		mausb_pr_err("Wrong network family provided");
+		status = -EINVAL;
+		goto callback;
+	}
+
+	status = kernel_connect(ip_ctx->client_socket, sa, sa_size, O_RDWR);
+	if (status < 0) {
+		mausb_pr_err("Failed to connect to host, status=%d", status);
+		goto clear_socket;
+	}
+
+	queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work);
+
+	goto callback;
+
+clear_socket:
+	sock_release(ip_ctx->client_socket);
+	ip_ctx->client_socket = NULL;
+callback:
+	ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel, MAUSB_LINK_CONNECT,
+			    status, NULL);
+}
+
+void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx)
+{
+	queue_work(ip_ctx->connect_workq, &ip_ctx->connect_work);
+}
+
+int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx)
+{
+	if (ip_ctx && ip_ctx->client_socket)
+		return kernel_sock_shutdown(ip_ctx->client_socket, SHUT_RDWR);
+	return 0;
+}
+
+int mausb_ip_send(struct mausb_ip_ctx *ip_ctx,
+		  struct mausb_kvec_data_wrapper *wrapper)
+{
+	struct msghdr msghd;
+
+	if (!ip_ctx) {
+		mausb_pr_alert("Socket ctx is NULL!");
+		return -EINVAL;
+	}
+
+	memset(&msghd, 0, sizeof(msghd));
+	msghd.msg_flags = MSG_WAITALL;
+
+	return kernel_sendmsg(ip_ctx->client_socket, &msghd, wrapper->kvec,
+			      wrapper->kvec_num, wrapper->length);
+}
+
+static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx *recv_ctx)
+{
+	recv_ctx->buffer   = NULL;
+	recv_ctx->left	   = 0;
+	recv_ctx->received = 0;
+}
+
+static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx *recv_ctx)
+{
+	kfree(recv_ctx->buffer);
+	__mausb_ip_recv_ctx_clear(recv_ctx);
+}
+
+static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx)
+{
+	struct msghdr msghd;
+	struct kvec vec;
+	int  status;
+	bool peek = true;
+	unsigned int optval = 1;
+	struct socket *client_socket = (struct socket *)ip_ctx->client_socket;
+
+	/* receive with timeout of 0.5s */
+	while (true) {
+		memset(&msghd, 0, sizeof(msghd));
+		if (peek) {
+			vec.iov_base = ip_ctx->recv_ctx.common_hdr;
+			vec.iov_len  = sizeof(ip_ctx->recv_ctx.common_hdr);
+			msghd.msg_flags = MSG_PEEK;
+		} else {
+			vec.iov_base =
+			    ip_ctx->recv_ctx.buffer +
+			    ip_ctx->recv_ctx.received;
+			vec.iov_len = ip_ctx->recv_ctx.left;
+			msghd.msg_flags = MSG_WAITALL;
+		}
+
+		if (!ip_ctx->udp) {
+			status = kernel_setsockopt(client_socket, IPPROTO_TCP,
+						   TCP_QUICKACK,
+						   (char *)&optval,
+						   sizeof(optval));
+			if (status != 0) {
+				mausb_pr_warn("Setting TCP_QUICKACK failed, status=%d",
+					      status);
+			}
+		}
+
+		status = kernel_recvmsg(client_socket, &msghd, &vec, 1,
+					vec.iov_len, (int)msghd.msg_flags);
+		if (status == -EAGAIN) {
+			return -EAGAIN;
+		} else if (status <= 0) {
+			mausb_pr_warn("kernel_recvmsg, status=%d", status);
+
+			__mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx);
+			ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel,
+					    MAUSB_LINK_RECV, status, NULL);
+			return status;
+		}
+
+		mausb_pr_debug("kernel_recvmsg, status=%d", status);
+
+		if (peek) {
+			if ((unsigned int)status <
+					sizeof(ip_ctx->recv_ctx.common_hdr))
+				return -EAGAIN;
+			/* length field of mausb_common_hdr */
+			ip_ctx->recv_ctx.left =
+			    *(u16 *)(&ip_ctx->recv_ctx.common_hdr[2]);
+			ip_ctx->recv_ctx.received = 0;
+			ip_ctx->recv_ctx.buffer	  =
+			    kzalloc(ip_ctx->recv_ctx.left, GFP_KERNEL);
+			peek = false;
+			if (!ip_ctx->recv_ctx.buffer) {
+				ip_ctx->fn_callback(ip_ctx->ctx,
+						    ip_ctx->channel,
+						    MAUSB_LINK_RECV,
+						    -ENOMEM, NULL);
+				return -ENOMEM;
+			}
+		} else {
+			if (status < ip_ctx->recv_ctx.left) {
+				ip_ctx->recv_ctx.left -= (u16)status;
+				ip_ctx->recv_ctx.received += (u16)status;
+			} else {
+				ip_ctx->fn_callback(ip_ctx->ctx,
+						    ip_ctx->channel,
+						    MAUSB_LINK_RECV, status,
+						    ip_ctx->recv_ctx.buffer);
+				__mausb_ip_recv_ctx_clear(&ip_ctx->recv_ctx);
+				peek = true;
+			}
+		}
+	}
+
+	return status;
+}
+
+static void __mausb_ip_recv_work(struct work_struct *work)
+{
+	struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx,
+						   recv_work);
+	int status = __mausb_ip_recv(ip_ctx);
+
+	if (status <= 0 && status != -EAGAIN)
+		return;
+
+	queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work);
+}
diff --git a/drivers/usb/mausb_host/ip_link.h b/drivers/usb/mausb_host/ip_link.h
new file mode 100644
index 000000000000..5946151e4e4e
--- /dev/null
+++ b/drivers/usb/mausb_host/ip_link.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_IP_LINK_H__
+#define __MAUSB_IP_LINK_H__
+
+#include <linux/inet.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <net/net_namespace.h>
+
+#define MAUSB_LINK_BUFF_SIZE	16777216
+#define MAUSB_LINK_TOS_LEVEL_EF 0xB8
+
+enum mausb_link_action {
+	MAUSB_LINK_CONNECT	= 0,
+	MAUSB_LINK_DISCONNECT	= 1,
+	MAUSB_LINK_RECV		= 2,
+	MAUSB_LINK_SEND		= 3
+};
+
+enum mausb_channel {
+	MAUSB_CTRL_CHANNEL  = 0,
+	MAUSB_ISOCH_CHANNEL = 1,
+	MAUSB_BULK_CHANNEL  = 2,
+	MAUSB_INTR_CHANNEL  = 3,
+	MAUSB_MGMT_CHANNEL  = 4
+};
+
+struct mausb_kvec_data_wrapper {
+	struct kvec *kvec;
+	u32    kvec_num;
+	u32    length;
+};
+
+struct mausb_ip_recv_ctx {
+	u16  left;
+	u16  received;
+	char *buffer;
+	char common_hdr[12] __aligned(4);
+};
+
+struct mausb_ip_ctx {
+	struct socket		*client_socket;
+	union {
+		struct sockaddr_in sa_in;
+#if IS_ENABLED(CONFIG_IPV6)
+		struct sockaddr_in6 sa_in6;
+#endif
+	} dev_addr_in;
+	struct net		*net_ns;
+	bool			udp;
+
+	/* Queues to schedule rx work */
+	struct workqueue_struct	*recv_workq;
+	struct workqueue_struct	*connect_workq;
+	struct work_struct	recv_work;
+	struct work_struct	connect_work;
+
+	struct mausb_ip_recv_ctx recv_ctx; /* recv buffer */
+
+	enum mausb_channel channel;
+	void *ctx;
+	/* callback should store task into hpal queue */
+	void (*fn_callback)(void *ctx, enum mausb_channel channel,
+			    enum mausb_link_action act, int status, void *data);
+};
+
+int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx,
+		      struct net *net_ns,
+		      char ip_addr[INET6_ADDRSTRLEN],
+		      u16 port,
+		      void *ctx,
+		      void (*ctx_callback)(void *ctx,
+					   enum mausb_channel channel,
+					   enum mausb_link_action act,
+					   int status, void *data),
+		      enum mausb_channel channel);
+int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx);
+int mausb_ip_send(struct mausb_ip_ctx *ip_ctx,
+		  struct mausb_kvec_data_wrapper *wrapper);
+
+void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx);
+void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx);
+
+#endif /* __MAUSB_IP_LINK_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 3/8] usb: mausb_host: HCD initialization
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 2/8] usb: mausb_host: Add link layer implementation vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented HCD initialization/deinitialization functionality.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile     |   1 +
 drivers/usb/mausb_host/hcd.c        | 188 ++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hcd.h        |  71 +++++++++++
 drivers/usb/mausb_host/mausb_core.c |  20 ++-
 4 files changed, 275 insertions(+), 5 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hcd.c
 create mode 100644 drivers/usb/mausb_host/hcd.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index 19445b73b50b..cce4696682b2 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
 mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
 mausb_host-y += ip_link.o
+mausb_host-y += hcd.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
new file mode 100644
index 000000000000..3aa548a6cb30
--- /dev/null
+++ b/drivers/usb/mausb_host/hcd.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hcd.h"
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+
+#include "utils.h"
+
+static int mausb_open(struct inode *inode, struct file *file);
+static int mausb_release(struct inode *inode, struct file *file);
+static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length,
+			  loff_t *offset);
+static ssize_t mausb_write(struct file *file, const char __user *buffer,
+			   size_t length, loff_t *offset);
+static long mausb_ioctl(struct file *file, unsigned int ioctl_func,
+			unsigned long ioctl_buffer);
+static int mausb_bus_probe(struct device *dev);
+static int mausb_bus_remove(struct device *dev);
+static int mausb_bus_match(struct device *dev, struct device_driver *drv);
+
+static const struct file_operations mausb_fops = {
+	.open		= mausb_open,
+	.release	= mausb_release,
+	.read		= mausb_read,
+	.write		= mausb_write,
+	.unlocked_ioctl	= mausb_ioctl
+};
+
+static unsigned int major;
+static unsigned int minor = 1;
+static dev_t devt;
+static struct device *device;
+
+struct mausb_hcd	*mhcd;
+static struct class	*mausb_class;
+static struct bus_type	mausb_bus_type = {
+	.name	= DEVICE_NAME,
+	.match	= mausb_bus_match,
+	.probe	= mausb_bus_probe,
+	.remove	= mausb_bus_remove,
+};
+
+static struct device_driver mausb_driver = {
+	.name	= DEVICE_NAME,
+	.bus	= &mausb_bus_type,
+	.owner	= THIS_MODULE,
+};
+
+static void mausb_remove(void)
+{
+	struct usb_hcd *hcd, *shared_hcd;
+
+	hcd	   = mhcd->hcd_hs_ctx->hcd;
+	shared_hcd = mhcd->hcd_ss_ctx->hcd;
+
+	if (shared_hcd) {
+		usb_remove_hcd(shared_hcd);
+		usb_put_hcd(shared_hcd);
+		mhcd->hcd_ss_ctx = NULL;
+	}
+
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+	mhcd->hcd_hs_ctx = NULL;
+}
+
+static int mausb_bus_probe(struct device *dev)
+{
+	return 0;
+}
+
+static int mausb_bus_remove(struct device *dev)
+{
+	return 0;
+}
+
+static int mausb_bus_match(struct device *dev, struct device_driver *drv)
+{
+	if (strncmp(dev->bus->name, drv->name, strlen(drv->name)))
+		return 0;
+	else
+		return 1;
+}
+
+static int mausb_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int mausb_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length,
+			  loff_t *offset)
+{
+	return 0;
+}
+
+static ssize_t mausb_write(struct file *file, const char __user *buffer,
+			   size_t length, loff_t *offset)
+{
+	return 0;
+}
+
+static long mausb_ioctl(struct file *file, unsigned int ioctl_func,
+			unsigned long ioctl_buffer)
+{
+	return 0;
+}
+
+int mausb_init_hcd(void)
+{
+	int retval;
+
+	retval = register_chrdev(0, DEVICE_NAME, &mausb_fops);
+	if (retval < 0) {
+		mausb_pr_err("Register_chrdev failed");
+		return retval;
+	}
+
+	major = (unsigned int)retval;
+	retval = bus_register(&mausb_bus_type);
+	if (retval) {
+		mausb_pr_err("Bus_register failed %d", retval);
+		goto bus_register_error;
+	}
+
+	mausb_class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(mausb_class)) {
+		mausb_pr_err("Class_create failed %ld", PTR_ERR(mausb_class));
+		goto class_error;
+	}
+
+	retval = driver_register(&mausb_driver);
+	if (retval) {
+		mausb_pr_err("Driver_register failed");
+		goto driver_register_error;
+	}
+
+	mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC);
+	if (!mhcd) {
+		retval = -ENOMEM;
+		goto mausb_hcd_alloc_failed;
+	}
+
+	devt = MKDEV(major, minor);
+	device = device_create(mausb_class, NULL, devt, mhcd, DEVICE_NAME);
+	if (IS_ERR(device)) {
+		mausb_pr_err("Device_create failed %ld", PTR_ERR(device));
+		goto device_create_error;
+	}
+
+	device->driver = &mausb_driver;
+
+	return retval;
+device_create_error:
+	kfree(mhcd);
+	mhcd = NULL;
+mausb_hcd_alloc_failed:
+	driver_unregister(&mausb_driver);
+driver_register_error:
+	class_destroy(mausb_class);
+class_error:
+	bus_unregister(&mausb_bus_type);
+bus_register_error:
+	unregister_chrdev(major, DEVICE_NAME);
+
+	return retval;
+}
+
+void mausb_deinit_hcd(void)
+{
+	if (mhcd) {
+		mausb_remove();
+		device_destroy(mausb_class, devt);
+		driver_unregister(&mausb_driver);
+		class_destroy(mausb_class);
+		bus_unregister(&mausb_bus_type);
+		unregister_chrdev(major, DEVICE_NAME);
+	}
+}
diff --git a/drivers/usb/mausb_host/hcd.h b/drivers/usb/mausb_host/hcd.h
new file mode 100644
index 000000000000..cac62ba1f1e2
--- /dev/null
+++ b/drivers/usb/mausb_host/hcd.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HCD_H__
+#define __MAUSB_HCD_H__
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#define DEVICE_NAME "mausb_host_hcd_dev"
+#define CLASS_NAME "mausb"
+
+#define NUMBER_OF_PORTS		15
+
+#define MAX_USB_DEVICE_DEPTH	6
+
+#define RESPONSE_TIMEOUT	5000
+
+#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
+#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
+
+enum mausb_device_type {
+	USBDEVICE = 0,
+	USB20HUB  = 1,
+	USB30HUB  = 2
+};
+
+enum mausb_device_speed {
+	LOW_SPEED	 = 0,
+	FULL_SPEED	 = 1,
+	HIGH_SPEED	 = 2,
+	SUPER_SPEED	 = 3,
+	SUPER_SPEED_PLUS = 4
+};
+
+struct mausb_hcd {
+	spinlock_t	lock;	/* Protect HCD during URB processing */
+	struct device	*pdev;
+	u16		connected_ports;
+
+	struct rb_root	mausb_urbs;
+	struct hub_ctx	*hcd_ss_ctx;
+	struct hub_ctx	*hcd_hs_ctx;
+	struct notifier_block power_state_listener;
+};
+
+struct mausb_dev {
+	u32		port_status;
+	struct rb_root	usb_devices;
+	u8		dev_speed;
+	void		*ma_dev;
+};
+
+struct hub_ctx {
+	struct mausb_hcd *mhcd;
+	struct usb_hcd	 *hcd;
+	struct mausb_dev ma_devs[NUMBER_OF_PORTS];
+};
+
+int mausb_init_hcd(void);
+void mausb_deinit_hcd(void);
+
+#endif /* __MAUSB_HCD_H__ */
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
index 8638dd0a4856..3ce90c29f6de 100644
--- a/drivers/usb/mausb_host/mausb_core.c
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -11,6 +11,7 @@
 #include <linux/moduleparam.h>
 #include <linux/net.h>
 
+#include "hcd.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
@@ -67,21 +68,30 @@ static const struct kernel_param_ops mausb_client_disconnect_ops = {
 
 static int mausb_host_init(void)
 {
-	int status = mausb_create_dev();
+	int status;
 
 	mausb_pr_info("Module load. Version=%s", MAUSB_DRIVER_VERSION);
+	status = mausb_init_hcd();
+	if (status < 0)
+		goto cleanup;
 
-	if (status < 0) {
-		mausb_pr_alert("Failed to load MAUSB module!");
-		return status;
-	}
+	status = mausb_create_dev();
+	if (status < 0)
+		goto cleanup_hcd;
 
 	return 0;
+
+cleanup_hcd:
+	mausb_deinit_hcd();
+cleanup:
+	mausb_pr_alert("Failed to load MAUSB module!");
+	return status;
 }
 
 static void mausb_host_exit(void)
 {
 	mausb_pr_info("Module unloading started...");
+	mausb_deinit_hcd();
 	mausb_cleanup_dev(1);
 	mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION);
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
                     ` (2 preceding siblings ...)
  2020-03-27 15:26   ` [PATCH v4 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-27 16:37     ` Alan Stern
  2020-03-27 15:26   ` [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
                     ` (4 subsequent siblings)
  8 siblings, 1 reply; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented handlers for subset of HCD events.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/hcd.c | 964 ++++++++++++++++++++++++++++++++++-
 drivers/usb/mausb_host/hcd.h |  86 +++-
 2 files changed, 1046 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
index 3aa548a6cb30..b20d1a36ba34 100644
--- a/drivers/usb/mausb_host/hcd.c
+++ b/drivers/usb/mausb_host/hcd.c
@@ -71,7 +71,7 @@ static void mausb_remove(void)
 
 static int mausb_bus_probe(struct device *dev)
 {
-	return 0;
+	return mausb_probe(dev);
 }
 
 static int mausb_bus_remove(struct device *dev)
@@ -159,7 +159,15 @@ int mausb_init_hcd(void)
 
 	device->driver = &mausb_driver;
 
+	retval = mausb_probe(device);
+	if (retval) {
+		mausb_pr_err("Mausb_probe failed");
+		goto mausb_probe_failed;
+	}
+
 	return retval;
+mausb_probe_failed:
+	device_destroy(mausb_class, devt);
 device_create_error:
 	kfree(mhcd);
 	mhcd = NULL;
@@ -186,3 +194,957 @@ void mausb_deinit_hcd(void)
 		unregister_chrdev(major, DEVICE_NAME);
 	}
 }
+
+static const char driver_name[] = "MA-USB host controller";
+
+static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length);
+static void mausb_set_port_feature(struct usb_hcd *hcd, u16 type_req,
+				   u16 value, u16 index, char *buff,
+				   u16 length);
+static void mausb_get_port_status(struct usb_hcd *hcd, u16 type_req,
+				  u16 value, u16 index, char *buff,
+				  u16 length);
+static void mausb_clear_port_feature(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length);
+static void mausb_get_hub_status(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length);
+static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			      struct usb_host_endpoint *endpoint);
+static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			       struct usb_host_endpoint *endpoint);
+static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev);
+static void mausb_endpoint_disable(struct usb_hcd *hcd,
+				   struct usb_host_endpoint *endpoint);
+static void mausb_endpoint_reset(struct usb_hcd *hcd,
+				 struct usb_host_endpoint *endpoint);
+static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_hcd_bus_resume(struct usb_hcd *hcd);
+static int mausb_hcd_bus_suspend(struct usb_hcd *hcd);
+static int mausb_hcd_get_frame_number(struct usb_hcd *hcd);
+static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length);
+static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff);
+static int mausb_hcd_reset(struct usb_hcd *hcd);
+static int mausb_hcd_start(struct usb_hcd *hcd);
+static void mausb_hcd_stop(struct usb_hcd *hcd);
+static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
+				   struct usb_tt *tt, gfp_t mem_flags);
+static void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
+
+static const struct hc_driver mausb_hc_driver = {
+	.description  =  driver_name,
+	.product_desc = driver_name,
+	.flags	      = HCD_USB3 | HCD_SHARED,
+
+	.hcd_priv_size = sizeof(struct hub_ctx),
+
+	.reset = mausb_hcd_reset,
+	.start = mausb_hcd_start,
+	.stop  = mausb_hcd_stop,
+
+	.get_frame_number = mausb_hcd_get_frame_number,
+
+	.hub_status_data   = mausb_hcd_hub_status,
+	.hub_control	   = mausb_hcd_hub_control,
+	.update_hub_device = mausb_hub_update_device,
+	.bus_suspend	   = mausb_hcd_bus_suspend,
+	.bus_resume	   = mausb_hcd_bus_resume,
+
+	.alloc_dev	= mausb_alloc_dev,
+	.free_dev	= mausb_free_dev,
+	.enable_device	= mausb_enable_device,
+	.update_device	= mausb_update_device,
+	.reset_device	= mausb_reset_device,
+
+	.add_endpoint	  = mausb_add_endpoint,
+	.drop_endpoint	  = mausb_drop_endpoint,
+	.check_bandwidth  = mausb_check_bandwidth,
+	.reset_bandwidth  = mausb_reset_bandwidth,
+	.address_device   = mausb_address_device,
+	.endpoint_disable = mausb_endpoint_disable,
+	.endpoint_reset	  = mausb_endpoint_reset,
+};
+
+static struct {
+	struct usb_bos_descriptor    bos;
+	struct usb_ss_cap_descriptor ss_cap;
+} usb3_bos_desc = {
+	.bos = {
+		.bLength	 = USB_DT_BOS_SIZE,
+		.bDescriptorType = USB_DT_BOS,
+		.wTotalLength	 = cpu_to_le16(sizeof(usb3_bos_desc)),
+		.bNumDeviceCaps	 = 1
+	},
+	.ss_cap = {
+		.bLength		= USB_DT_USB_SS_CAP_SIZE,
+		.bDescriptorType	= USB_DT_DEVICE_CAPABILITY,
+		.bDevCapabilityType	= USB_SS_CAP_TYPE,
+		.wSpeedSupported	= cpu_to_le16(USB_5GBPS_OPERATION),
+		.bFunctionalitySupport	= ilog2(USB_5GBPS_OPERATION)
+	}
+};
+
+static int get_root_hub_port_number(struct usb_device *dev, u8 *port_number)
+{
+	struct usb_device *first_hub_device = dev;
+
+	if (!dev->parent) {
+		(*port_number) = 0;
+		return -EINVAL;
+	}
+
+	while (first_hub_device->parent->parent)
+		first_hub_device = first_hub_device->parent;
+
+	(*port_number) = first_hub_device->portnum - 1;
+
+	return 0;
+}
+
+static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
+							*mdevs, void *dev_addr)
+{
+	struct rb_node *node = mdevs->usb_devices.rb_node;
+
+	while (node) {
+		struct mausb_usb_device_ctx *usb_device =
+		    rb_entry(node, struct mausb_usb_device_ctx, rb_node);
+
+		if (dev_addr < usb_device->dev_addr)
+			node = usb_device->rb_node.rb_left;
+		else if (dev_addr > usb_device->dev_addr)
+			node = usb_device->rb_node.rb_right;
+		else
+			return usb_device;
+	}
+	return NULL;
+}
+
+static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+	return 0;
+}
+
+static int mausb_hcd_reset(struct usb_hcd *hcd)
+{
+	if (usb_hcd_is_primary_hcd(hcd)) {
+		hcd->speed = HCD_USB2;
+		hcd->self.root_hub->speed = USB_SPEED_HIGH;
+	} else {
+		hcd->speed = HCD_USB3;
+		hcd->self.root_hub->speed = USB_SPEED_SUPER;
+	}
+	hcd->self.no_sg_constraint = 1;
+	hcd->self.sg_tablesize = UINT_MAX;
+
+	return 0;
+}
+
+static int mausb_hcd_start(struct usb_hcd *hcd)
+{
+	hcd->power_budget = 0;
+	hcd->uses_new_polling = 1;
+	return 0;
+}
+
+static void mausb_hcd_stop(struct usb_hcd *hcd)
+{
+	mausb_pr_debug("Not implemented");
+}
+
+static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff)
+{
+	int retval;
+	int changed;
+	int i;
+	struct hub_ctx *hub;
+	unsigned long flags = 0;
+
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	retval  = DIV_ROUND_UP(NUMBER_OF_PORTS + 1, 8);
+	changed = 0;
+
+	memset(buff, 0, (unsigned int)retval);
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		mausb_pr_info("hcd not accessible, hcd speed=%d", hcd->speed);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return 0;
+	}
+
+	for (i = 0; i < NUMBER_OF_PORTS; ++i) {
+		if ((hub->ma_devs[i].port_status & PORT_C_MASK)) {
+			buff[(i + 1) / 8] |= 1 << (i + 1) % 8;
+			changed = 1;
+		}
+	}
+
+	mausb_pr_info("Usb %d.0 : changed=%d, retval=%d",
+		      (hcd->speed == HCD_USB2) ? 2 : 3, changed, retval);
+
+	if (hcd->state == HC_STATE_SUSPENDED && changed == 1) {
+		mausb_pr_info("hcd state is suspended");
+		usb_hcd_resume_root_hub(hcd);
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return changed ? retval : 0;
+}
+
+static int mausb_hcd_bus_resume(struct usb_hcd *hcd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		mausb_pr_info("hcd not accessible, hcd speed=%d", hcd->speed);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return -ESHUTDOWN;
+	}
+	hcd->state = HC_STATE_RUNNING;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static int mausb_hcd_bus_suspend(struct usb_hcd *hcd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	hcd->state = HC_STATE_SUSPENDED;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length)
+{
+	int retval = 0;
+	struct hub_ctx	 *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_hcd *hub_mhcd = hub->mhcd;
+	unsigned long	 flags;
+	bool invalid_rhport = false;
+
+	index = ((__u8)(index & 0x00ff));
+	if (index < 1 || index > NUMBER_OF_PORTS)
+		invalid_rhport = true;
+
+	mausb_pr_info("TypeReq=%d", type_req);
+
+	spin_lock_irqsave(&hub_mhcd->lock, flags);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		mausb_pr_info("hcd not accessible, hcd speed=%d", hcd->speed);
+		spin_unlock_irqrestore(&hub_mhcd->lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	switch (type_req) {
+	case ClearHubFeature:
+		break;
+	case ClearPortFeature:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_clear_port_feature(hcd, type_req, value, index, buff,
+					 length);
+		break;
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		memcpy(buff, &usb3_bos_desc, sizeof(usb3_bos_desc));
+		retval = sizeof(usb3_bos_desc);
+		break;
+	case GetHubDescriptor:
+		mausb_get_hub_descriptor(hcd, type_req, value, index, buff,
+					 length);
+		break;
+	case GetHubStatus:
+		mausb_get_hub_status(hcd, type_req, value, index, buff,
+				     length);
+		break;
+	case GetPortStatus:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_get_port_status(hcd, type_req, value, index, buff,
+				      length);
+		break;
+	case SetHubFeature:
+		retval = -EPIPE;
+		break;
+	case SetPortFeature:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_set_port_feature(hcd, type_req, value, index, buff,
+				       length);
+		break;
+	default:
+		retval = -EPIPE;
+	}
+
+invalid_port:
+	spin_unlock_irqrestore(&hub_mhcd->lock, flags);
+	return retval;
+}
+
+int mausb_probe(struct device *dev)
+{
+	struct mausb_hcd *mausb_hcd;
+	struct usb_hcd	 *hcd_ss;
+	struct usb_hcd	 *hcd_hs;
+	int ret;
+
+	mausb_hcd = dev_get_drvdata(dev);
+	spin_lock_init(&mausb_hcd->lock);
+
+	hcd_hs = usb_create_hcd(&mausb_hc_driver, dev, dev_name(dev));
+	if (!hcd_hs)
+		return -ENOMEM;
+
+	hcd_hs->has_tt = 1;
+	mausb_hcd->hcd_hs_ctx	    = (struct hub_ctx *)hcd_hs->hcd_priv;
+	mausb_hcd->hcd_hs_ctx->mhcd = mausb_hcd;
+	mausb_hcd->hcd_hs_ctx->hcd  = hcd_hs;
+	memset(mausb_hcd->hcd_hs_ctx->ma_devs, 0,
+	       sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
+
+	ret = usb_add_hcd(hcd_hs, 0, 0);
+	if (ret) {
+		mausb_pr_err("usb_add_hcd failed");
+		goto put_hcd_hs;
+	}
+
+	hcd_ss = usb_create_shared_hcd(&mausb_hc_driver, dev, dev_name(dev),
+				       hcd_hs);
+	if (!hcd_ss) {
+		ret = -ENOMEM;
+		goto remove_hcd_hs;
+	}
+	mausb_hcd->hcd_ss_ctx	    = (struct hub_ctx *)hcd_ss->hcd_priv;
+	mausb_hcd->hcd_ss_ctx->mhcd = mausb_hcd;
+	mausb_hcd->hcd_ss_ctx->hcd  = hcd_ss;
+
+	memset(mausb_hcd->hcd_ss_ctx->ma_devs, 0,
+	       sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
+
+	ret = usb_add_hcd(hcd_ss, 0, 0);
+	if (ret) {
+		mausb_pr_err("usb_add_hcd failed");
+		goto put_hcd_ss;
+	}
+
+	return ret;
+
+put_hcd_ss:
+	usb_put_hcd(hcd_ss);
+remove_hcd_hs:
+	usb_remove_hcd(hcd_hs);
+put_hcd_hs:
+	usb_put_hcd(hcd_hs);
+	mausb_hcd->hcd_hs_ctx = NULL;
+	mausb_hcd->hcd_ss_ctx = NULL;
+	return ret;
+}
+
+static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length)
+{
+	u8 width;
+	struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buff;
+
+	memset(desc, 0, sizeof(struct usb_hub_descriptor));
+
+	if (hcd->speed == HCD_USB3) {
+		desc->bDescriptorType	   = USB_DT_SS_HUB;
+		desc->bDescLength	   = 12;
+		desc->wHubCharacteristics  =
+		    cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+		desc->bNbrPorts		   = NUMBER_OF_PORTS;
+		desc->u.ss.bHubHdrDecLat   = 0x04;
+		desc->u.ss.DeviceRemovable = cpu_to_le16(0xffff);
+	} else {
+		desc->bDescriptorType	  = USB_DT_HUB;
+		desc->wHubCharacteristics =
+		    cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+		desc->bNbrPorts		  = NUMBER_OF_PORTS;
+		width			  = (u8)(desc->bNbrPorts / 8 + 1);
+		desc->bDescLength	  = USB_DT_HUB_NONVAR_SIZE + 2 * width;
+
+		memset(&desc->u.hs.DeviceRemovable[0], 0, width);
+		memset(&desc->u.hs.DeviceRemovable[width], 0xff, width);
+	}
+}
+
+static void mausb_set_port_feature(struct usb_hcd *hcd, u16 type_req,
+				   u16 value, u16 index, char *buff,
+				   u16 length)
+{
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	switch (value) {
+	case USB_PORT_FEAT_LINK_STATE:
+		mausb_pr_debug("USB_PORT_FEAT_LINK_STATE");
+		if (hcd->speed == HCD_USB3) {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		} else {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		}
+		break;
+	case USB_PORT_FEAT_U1_TIMEOUT:
+	case USB_PORT_FEAT_U2_TIMEOUT:
+		break;
+	case USB_PORT_FEAT_SUSPEND:
+		break;
+	case USB_PORT_FEAT_POWER:
+		mausb_pr_debug("USB_PORT_FEAT_POWER");
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status |=
+			    USB_SS_PORT_STAT_POWER;
+		} else {
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_POWER;
+		}
+		break;
+	case USB_PORT_FEAT_BH_PORT_RESET:
+		mausb_pr_debug("USB_PORT_FEAT_BH_PORT_RESET");
+		/* fall through */
+	case USB_PORT_FEAT_RESET:
+		mausb_pr_debug("USB_PORT_FEAT_RESET hcd->speed=%d", hcd->speed);
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status = 0;
+			hub->ma_devs[index - 1].port_status =
+			    (USB_SS_PORT_STAT_POWER |
+			     USB_PORT_STAT_CONNECTION | USB_PORT_STAT_RESET);
+		} else if (hub->ma_devs[index - 1].port_status
+			   & USB_PORT_STAT_ENABLE) {
+			hub->ma_devs[index - 1].port_status &=
+			    ~(USB_PORT_STAT_ENABLE |
+			      USB_PORT_STAT_LOW_SPEED |
+			      USB_PORT_STAT_HIGH_SPEED);
+		}
+		/* fall through */
+	default:
+		mausb_pr_info("Default value=%d", value);
+
+		if (hcd->speed == HCD_USB3) {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		} else {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		}
+	}
+}
+
+static void mausb_get_port_status(struct usb_hcd *hcd, u16 type_req,
+				  u16 value, u16 index, char *buff,
+				  u16 length)
+{
+	u8 dev_speed;
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	if ((hub->ma_devs[index - 1].port_status &
+				(1 << USB_PORT_FEAT_RESET)) != 0) {
+		mausb_pr_info("Finished reset hcd->speed=%d", hcd->speed);
+
+		dev_speed = hub->ma_devs[index - 1].dev_speed;
+		switch (dev_speed) {
+		case LOW_SPEED:
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_LOW_SPEED;
+			break;
+		case HIGH_SPEED:
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_HIGH_SPEED;
+			break;
+		default:
+			mausb_pr_info("Not updating port_status for device speed %d",
+				      dev_speed);
+		}
+
+		hub->ma_devs[index - 1].port_status |=
+		    (1 << USB_PORT_FEAT_C_RESET) | USB_PORT_STAT_ENABLE;
+		hub->ma_devs[index - 1].port_status &=
+		    ~(1 << USB_PORT_FEAT_RESET);
+	}
+
+	((__le16 *)buff)[0] = cpu_to_le16(hub->ma_devs[index - 1].port_status);
+	((__le16 *)buff)[1] =
+	    cpu_to_le16(hub->ma_devs[index - 1].port_status >> 16);
+
+	mausb_pr_info("port_status=%d", hub->ma_devs[index - 1].port_status);
+}
+
+static void mausb_clear_port_feature(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length)
+{
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	switch (value) {
+	case USB_PORT_FEAT_SUSPEND:
+		break;
+	case USB_PORT_FEAT_POWER:
+		mausb_pr_debug("USB_PORT_FEAT_POWER");
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status &=
+			    ~USB_SS_PORT_STAT_POWER;
+		} else {
+			hub->ma_devs[index - 1].port_status &=
+			    ~USB_PORT_STAT_POWER;
+		}
+		break;
+	case USB_PORT_FEAT_RESET:
+
+	case USB_PORT_FEAT_C_RESET:
+
+	default:
+		mausb_pr_info("Default value: %d", value);
+
+		hub->ma_devs[index - 1].port_status &= ~(1 << value);
+	}
+}
+
+static void mausb_get_hub_status(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length)
+{
+	*(__le32 *)buff = cpu_to_le32(0);
+}
+
+static int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	mausb_pr_info("Usb device=%p", dev);
+
+	return 1;
+}
+
+static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number;
+	s16	dev_handle;
+	int	status;
+	struct hub_ctx   *hub  = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_dev	    *mdev = NULL;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct mausb_endpoint_ctx   *ep_ctx = dev->ep0.hcpriv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return;
+	}
+
+	mdev  = &hub->ma_devs[port_number];
+
+	usb_device_ctx = mausb_find_usb_device(mdev, dev);
+	if (!usb_device_ctx) {
+		mausb_pr_warn("device_ctx is not found");
+		return;
+	}
+
+	dev_handle = usb_device_ctx->dev_handle;
+
+	if (ep_ctx) {
+		dev->ep0.hcpriv = NULL;
+		kfree(ep_ctx);
+
+	} else {
+		mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle);
+	}
+
+	rb_erase(&usb_device_ctx->rb_node, &mdev->usb_devices);
+	mausb_pr_info("USB device deleted device=%p", usb_device_ctx->dev_addr);
+	kfree(usb_device_ctx);
+}
+
+/*
+ * For usb 2.0 logitech camera called multiple times, once during
+ * enumeration of device and later after mausb_reset_device.
+ */
+static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number;
+	int	status;
+	struct hub_ctx	*hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_warn("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx)
+		return -ENODEV;
+
+	mausb_pr_info("dev_handle=%#x, dev_speed=%#x",
+		      usb_device_ctx->dev_handle, dev->speed);
+
+	if (dev->speed >= USB_SPEED_SUPER)
+		mausb_pr_info("USB 3.0");
+	else
+		mausb_pr_info("USB 2.0");
+
+	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED) {
+		status = mausb_enable_device(hcd, dev);
+		if (status < 0)
+			return status;
+	}
+
+	endpoint_ctx = dev->ep0.hcpriv;
+	if (!endpoint_ctx) {
+		mausb_pr_err("endpoint_ctx is NULL: dev_handle=%#x",
+			     usb_device_ctx->dev_handle);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			      struct usb_host_endpoint *endpoint)
+{
+	int	status;
+	u8	port_number;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_dev_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return 0;
+	}
+
+	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!usb_dev_ctx) {
+		mausb_pr_warn("Device not found");
+		return -ENODEV;
+	}
+
+	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
+	if (!endpoint_ctx)
+		return -ENOMEM;
+
+	endpoint_ctx->dev_handle	= usb_dev_ctx->dev_handle;
+	endpoint_ctx->usb_device_ctx	= usb_dev_ctx;
+	endpoint->hcpriv		= endpoint_ctx;
+
+	return 0;
+}
+
+static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			       struct usb_host_endpoint *endpoint)
+{
+	u8	port_number;
+	int	status;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_dev_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx = endpoint->hcpriv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!endpoint_ctx) {
+		mausb_pr_err("Endpoint context doesn't exist");
+		return 0;
+	}
+	if (!usb_dev_ctx) {
+		mausb_pr_err("Usb device context doesn't exist");
+		return -ENODEV;
+	}
+
+	endpoint->hcpriv = NULL;
+	kfree(endpoint_ctx);
+	return 0;
+}
+
+/*
+ * For usb 2.0 logitech camera called multiple times, once during enumeration
+ * of device and later after mausb_reset_device. In latter case it is
+ * required to address the device again in order for ep0 to work properly.
+ */
+static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	int status;
+	u8 port_number;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx)
+		return -ENODEV;
+
+	mausb_pr_info("Device assigned and addressed usb_device_ctx=%p",
+		      usb_device_ctx);
+
+	return 0;
+}
+
+static int mausb_is_hub_device(struct usb_device *dev)
+{
+	return dev->descriptor.bDeviceClass == 0x09;
+}
+
+static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number = 0;
+	int	status	    = 0;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+
+	if (mausb_is_hub_device(dev)) {
+		mausb_pr_warn("Device is hub");
+		return 0;
+	}
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		mausb_pr_warn("Device not found");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
+				   struct usb_tt *tt, gfp_t mem_flags)
+{
+	int	status;
+	u8	port_number;
+	u16 max_exit_latency = 0;
+	u8  mtt = 0;
+	u8  ttt = 0;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	if (dev->speed == USB_SPEED_HIGH) {
+		mtt = tt->multi == 0 ? 1 : 0;
+		ttt = (u8)tt->think_time;
+	}
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return 0;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
+					       dev);
+
+	if (!usb_device_ctx) {
+		mausb_pr_err("USB device not found");
+		return -ENODEV;
+	}
+
+	if (dev->usb3_lpm_u1_enabled)
+		max_exit_latency = (u16)dev->u1_params.mel;
+	else if (dev->usb3_lpm_u2_enabled)
+		max_exit_latency = (u16)dev->u2_params.mel;
+
+	return 0;
+}
+
+static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	mausb_pr_debug("Not implemented");
+	return 0;
+}
+
+static void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	mausb_pr_debug("Not implemented");
+}
+
+static void mausb_endpoint_disable(struct usb_hcd *hcd,
+				   struct usb_host_endpoint *endpoint)
+{
+	mausb_pr_debug("Not implemented");
+}
+
+static void mausb_endpoint_reset(struct usb_hcd *hcd,
+				 struct usb_host_endpoint *endpoint)
+{
+	int status;
+	int is_control;
+	int epnum;
+	int is_out;
+	u16	dev_handle;
+	u8	tsp;
+	u8	port_number;
+	struct hub_ctx		    *hub;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct usb_device	    *dev;
+	struct mausb_endpoint_ctx   *ep_ctx;
+
+	ep_ctx = endpoint->hcpriv;
+	if (!ep_ctx) {
+		mausb_pr_err("ep->hcpriv is NULL");
+		return;
+	}
+
+	usb_device_ctx	= ep_ctx->usb_device_ctx;
+	dev_handle	= usb_device_ctx->dev_handle;
+	dev		= usb_device_ctx->dev_addr;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return;
+	}
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	is_control = usb_endpoint_xfer_control(&endpoint->desc);
+	epnum = usb_endpoint_num(&endpoint->desc);
+	is_out = usb_endpoint_dir_out(&endpoint->desc);
+	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
+
+	if (status < 0)
+		return;
+
+	if (status != EUCLEAN) {
+		if (!tsp) {
+			usb_settoggle(dev, epnum, is_out, 0U);
+			if (is_control)
+				usb_settoggle(dev, epnum, !is_out, 0U);
+		}
+
+		return;
+	}
+
+	if (tsp)
+		return;
+
+	mausb_pr_info("ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+}
+
+/*
+ * For usb 2.0 logitech camera called multiple times,
+ * followed by either mausb_enable_device or mausb_address_device.
+ * Resets device to non-addressed state.
+ */
+static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	int status;
+	u8  port_number;
+	u16 dev_handle;
+	struct hub_ctx		    *hub;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!usb_device_ctx ||
+	    usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
+		return 0;
+
+	dev_handle = usb_device_ctx->dev_handle;
+
+	return 0;
+}
+
+void mausb_clear_hcd_madev(u16 port_number)
+{
+	unsigned long flags = 0;
+
+	if (port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_err("port_number out of range, port_number=%x",
+			     port_number);
+		return;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	memset(&mhcd->hcd_hs_ctx->ma_devs[port_number], 0,
+	       sizeof(struct mausb_dev));
+	memset(&mhcd->hcd_ss_ctx->ma_devs[port_number], 0,
+	       sizeof(struct mausb_dev));
+
+	mhcd->connected_ports &= ~(1 << port_number);
+
+	mhcd->hcd_hs_ctx->ma_devs[port_number].port_status =
+							USB_PORT_STAT_POWER;
+	mhcd->hcd_ss_ctx->ma_devs[port_number].port_status =
+							USB_SS_PORT_STAT_POWER;
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+}
diff --git a/drivers/usb/mausb_host/hcd.h b/drivers/usb/mausb_host/hcd.h
index cac62ba1f1e2..cbef70a2f985 100644
--- a/drivers/usb/mausb_host/hcd.h
+++ b/drivers/usb/mausb_host/hcd.h
@@ -24,9 +24,6 @@
 
 #define RESPONSE_TIMEOUT	5000
 
-#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
-#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
-
 enum mausb_device_type {
 	USBDEVICE = 0,
 	USB20HUB  = 1,
@@ -68,4 +65,87 @@ struct hub_ctx {
 int mausb_init_hcd(void);
 void mausb_deinit_hcd(void);
 
+#define PORT_C_MASK \
+		((USB_PORT_STAT_C_CONNECTION \
+		| USB_PORT_STAT_C_ENABLE \
+		| USB_PORT_STAT_C_SUSPEND \
+		| USB_PORT_STAT_C_OVERCURRENT \
+		| USB_PORT_STAT_C_RESET) << 16)
+
+#define MAUSB_PORT_20_STATUS_CONNECT         0x0001
+#define MAUSB_PORT_20_STATUS_ENABLE          0x0002
+#define MAUSB_PORT_20_STATUS_SUSPEND         0x0004
+#define MAUSB_PORT_20_STATUS_OVER_CURRENT    0x0008
+#define MAUSB_PORT_20_STATUS_RESET           0x0010
+#define MAUSB_PORT_20_STATUS_POWER           0x0100
+#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
+#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
+
+#define MAUSB_CHANGE_PORT_20_STATUS_CONNECT  0x010000
+#define MAUSB_CHANGE_PORT_20_STATUS_RESET    0x100000
+
+/* USB 3.2 specification chapter 10.16.2.6.1 table 10-13 page 440 */
+#define MAUSB_PORT_30_STATUS_CONNECT              0x0001
+#define MAUSB_PORT_30_STATUS_ENABLE               0x0002
+#define MAUSB_PORT_30_STATUS_OVER_CURRENT         0x0008
+#define MAUSB_PORT_30_STATUS_RESET                0x0010
+#define MAUSB_PORT_30_LINK_STATE_U0               0x0000
+#define MAUSB_PORT_30_LINK_STATE_U1               0x0020
+#define MAUSB_PORT_30_LINK_STATE_U2               0x0040
+#define MAUSB_PORT_30_LINK_STATE_U3               0x0060
+#define MAUSB_PORT_30_LINK_STATE_DISABLED         0x0080
+#define MAUSB_PORT_30_LINK_STATE_RX_DETECT        0x00A0
+#define MAUSB_PORT_30_LINK_STATE_INACTIVE         0x00C0
+#define MAUSB_PORT_30_LINK_STATE_POLLING          0x00E0
+#define MAUSB_PORT_30_LINK_STATE_RECOVERY         0x0100
+#define MAUSB_PORT_30_LINK_STATE_HOT_RESET        0x0120
+#define MAUSB_PORT_30_LINK_STATE_COMPLIANCE_MODE  0x0140
+#define MAUSB_PORT_30_LINK_STATE_LOOPBACK         0x0160
+#define MAUSB_PORT_30_STATUS_POWER                0x0200
+#define MAUSB_PORT_30_STATUS_SUPER_SPEED          0x0400
+#define MAUSB_PORT_30_CLEAR_LINK_STATE            0xFE1F
+
+/* USB 3.2 specification chapter 10.16.2.6.2 table 10-14 page 443 */
+#define MAUSB_CHANGE_PORT_30_STATUS_CONNECT              0x010000
+#define MAUSB_CHANGE_PORT_30_STATUS_OVER_CURRENT         0x080000
+#define MAUSB_CHANGE_PORT_30_STATUS_RESET                0x100000
+#define MAUSB_CHANGE_PORT_30_BH_STATUS_RESET             0x200000
+#define MAUSB_CHANGE_PORT_30_LINK_STATE                  0x400000
+#define MAUSB_CHANGE_PORT_30_CONFIG_ERROR                0x800000
+
+/* USB 3.2 specification chapter 10.16.2.4 table 10-10 page 438 */
+#define MAUSB_HUB_30_POWER_GOOD              0x00
+#define MAUSB_HUB_30_LOCAL_POWER_SOURCE_LOST 0x01
+#define MAUSB_HUB_30_OVER_CURRENT            0x02
+
+/* USB 3.2 specification chapter 10.16.2.4 table 10-11 page 438 */
+#define MAUSB_CHANGE_HUB_30_LOCAL_POWER_SOURCE_LOST  0x10000
+#define MAUSB_CHANGE_HUB_30_OVER_CURRENT             0x20000
+
+#define DEV_HANDLE_NOT_ASSIGNED	-1
+
+struct mausb_usb_device_ctx {
+	s32		dev_handle;
+	bool		addressed;
+	void		*dev_addr;
+	struct rb_node	rb_node;
+};
+
+struct mausb_endpoint_ctx {
+	u16	ep_handle;
+	u16	dev_handle;
+	void	*ma_dev;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+};
+
+struct mausb_urb_ctx {
+	struct urb		*urb;
+	struct rb_node		rb_node;
+	struct work_struct	work;
+};
+
+int mausb_probe(struct device *dev);
+
+void mausb_clear_hcd_madev(u16 port_number);
+
 #endif /* __MAUSB_HCD_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
                     ` (3 preceding siblings ...)
  2020-03-27 15:26   ` [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-27 16:35     ` Alan Stern
  2020-03-28  3:56     ` kbuild test robot
  2020-03-27 15:26   ` [PATCH v4 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication vladimir.stankovic
                     ` (3 subsequent siblings)
  8 siblings, 2 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Protocol adaptation layer (PAL) implementation has been added to
introduce MA-USB structures and logic.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile        |    1 +
 drivers/usb/mausb_host/hcd.c           |  523 ++++++++++-
 drivers/usb/mausb_host/hcd.h           |   11 +
 drivers/usb/mausb_host/hpal.c          | 1094 ++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal.h          |  289 +++++++
 drivers/usb/mausb_host/ma_usb.h        |  869 +++++++++++++++++++
 drivers/usb/mausb_host/mausb_address.h |   26 +
 drivers/usb/mausb_host/mausb_core.c    |   13 +-
 drivers/usb/mausb_host/mausb_event.h   |  224 +++++
 9 files changed, 3040 insertions(+), 10 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal.c
 create mode 100644 drivers/usb/mausb_host/hpal.h
 create mode 100644 drivers/usb/mausb_host/ma_usb.h
 create mode 100644 drivers/usb/mausb_host/mausb_address.h
 create mode 100644 drivers/usb/mausb_host/mausb_event.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index cce4696682b2..829314b15cbb 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -10,5 +10,6 @@ mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
 mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
+mausb_host-y += hpal.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
index b20d1a36ba34..70cb633c39ba 100644
--- a/drivers/usb/mausb_host/hcd.c
+++ b/drivers/usb/mausb_host/hcd.c
@@ -9,6 +9,7 @@
 #include <linux/limits.h>
 #include <linux/module.h>
 
+#include "hpal.h"
 #include "utils.h"
 
 static int mausb_open(struct inode *inode, struct file *file);
@@ -195,6 +196,90 @@ void mausb_deinit_hcd(void)
 	}
 }
 
+void mausb_port_has_changed(const enum mausb_device_type device_type,
+			    const enum mausb_device_speed device_speed,
+			    void *ma_dev)
+{
+	struct usb_hcd *hcd;
+	unsigned long flags = 0;
+	struct mausb_device *dev = ma_dev;
+	u16 port_number = dev->port_number;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+		    USB_PORT_STAT_CONNECTION | (1 <<
+						USB_PORT_FEAT_C_CONNECTION);
+
+		if (device_speed == LOW_SPEED) {
+			mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			    MAUSB_PORT_20_STATUS_LOW_SPEED;
+			mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
+			    LOW_SPEED;
+		} else if (device_speed == HIGH_SPEED) {
+			mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			    MAUSB_PORT_20_STATUS_HIGH_SPEED;
+			mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
+			    HIGH_SPEED;
+		}
+
+		hcd = mhcd->hcd_hs_ctx->hcd;
+		mhcd->hcd_hs_ctx->ma_devs[port_number].ma_dev = ma_dev;
+	} else {
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
+		    USB_PORT_STAT_CONNECTION | (1 <<
+						USB_PORT_FEAT_C_CONNECTION);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].dev_speed = SUPER_SPEED;
+
+		hcd = mhcd->hcd_ss_ctx->hcd;
+		mhcd->hcd_ss_ctx->ma_devs[port_number].ma_dev = ma_dev;
+	}
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	usb_hcd_poll_rh_status(hcd);
+}
+
+void mausb_hcd_disconnect(const u16 port_number,
+			  const enum mausb_device_type device_type,
+			  const enum mausb_device_speed device_speed)
+{
+	struct usb_hcd *hcd;
+	unsigned long flags = 0;
+
+	if (port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_err("port number out of range, port_number=%x",
+			     port_number);
+		return;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_CONNECTION);
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_ENABLE);
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			(1 << USB_PORT_FEAT_C_CONNECTION);
+		hcd = mhcd->hcd_hs_ctx->hcd;
+	} else {
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_CONNECTION);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_ENABLE);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
+			(1 << USB_PORT_FEAT_C_CONNECTION);
+		hcd = mhcd->hcd_ss_ctx->hcd;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+	if (!hcd)
+		return;
+
+	usb_hcd_poll_rh_status(hcd);
+}
+
 static const char driver_name[] = "MA-USB host controller";
 
 static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
@@ -235,12 +320,31 @@ static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff);
 static int mausb_hcd_reset(struct usb_hcd *hcd);
 static int mausb_hcd_start(struct usb_hcd *hcd);
 static void mausb_hcd_stop(struct usb_hcd *hcd);
+static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				 int status);
+static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				 gfp_t mem_flags);
 static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 				   struct usb_tt *tt, gfp_t mem_flags);
 static void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev);
 static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
 static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
 
+static void mausb_print_urb(struct urb *request)
+{
+	mausb_pr_debug("URB: urb=%p, ep_handle=%#x, packet_num=%d, setup_dma=%lld, is_setup_packet=%d, is_ep=%d, is_sg=%d, num_sgs=%d, num_mapped_sgs=%d, status=%d, is_transfer_buffer=%d, transfer_buffer_length=%d, is_transfer_dma=%llu, transfer_flags=%d, is_hcpriv=%d",
+		       request, ((struct mausb_endpoint_ctx *)
+				 request->ep->hcpriv)->ep_handle,
+		       request->number_of_packets, request->setup_dma,
+		       request->setup_packet ? 1 : 0, request->ep ? 1 : 0,
+		       request->sg ? 1 : 0, request->num_sgs,
+		       request->num_mapped_sgs, request->status,
+		       request->transfer_buffer ? 1 : 0,
+		       request->transfer_buffer_length,
+		       request->transfer_dma, request->transfer_flags,
+		       (request->ep && request->ep->hcpriv) ? 1 : 0);
+}
+
 static const struct hc_driver mausb_hc_driver = {
 	.description  =  driver_name,
 	.product_desc = driver_name,
@@ -252,6 +356,9 @@ static const struct hc_driver mausb_hc_driver = {
 	.start = mausb_hcd_start,
 	.stop  = mausb_hcd_stop,
 
+	.urb_enqueue = mausb_hcd_urb_enqueue,
+	.urb_dequeue = mausb_hcd_urb_dequeue,
+
 	.get_frame_number = mausb_hcd_get_frame_number,
 
 	.hub_status_data   = mausb_hcd_hub_status,
@@ -311,6 +418,25 @@ static int get_root_hub_port_number(struct usb_device *dev, u8 *port_number)
 	return 0;
 }
 
+static int usb_to_mausb_device_speed(u8 speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return MA_USB_SPEED_LOW_SPEED;
+	case USB_SPEED_FULL:
+		return MA_USB_SPEED_FULL_SPEED;
+	case USB_SPEED_WIRELESS:
+	case USB_SPEED_HIGH:
+		return MA_USB_SPEED_HIGH_SPEED;
+	case USB_SPEED_SUPER:
+		return MA_USB_SPEED_SUPER_SPEED;
+	case USB_SPEED_SUPER_PLUS:
+		return MA_USB_SPEED_SUPER_SPEED_PLUS;
+	default:
+		return -EINVAL;
+	}
+}
+
 static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
 							*mdevs, void *dev_addr)
 {
@@ -330,6 +456,31 @@ static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
 	return NULL;
 }
 
+static int mausb_insert_usb_device(struct mausb_dev *mdevs,
+				   struct mausb_usb_device_ctx *usb_device)
+{
+	struct rb_node **new_node = &mdevs->usb_devices.rb_node;
+	struct rb_node *parent = NULL;
+	struct mausb_usb_device_ctx *current_usb_device = NULL;
+
+	while (*new_node) {
+		parent = *new_node;
+		current_usb_device = rb_entry(*new_node,
+					      struct mausb_usb_device_ctx,
+					      rb_node);
+
+		if (usb_device->dev_addr < current_usb_device->dev_addr)
+			new_node = &((*new_node)->rb_left);
+		else if (usb_device->dev_addr > current_usb_device->dev_addr)
+			new_node = &((*new_node)->rb_right);
+		else
+			return -EEXIST;
+	}
+	rb_link_node(&usb_device->rb_node, parent, new_node);
+	rb_insert_color(&usb_device->rb_node, &mdevs->usb_devices);
+	return 0;
+}
+
 static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
 {
 	return 0;
@@ -504,6 +655,123 @@ static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
 	return retval;
 }
 
+static int mausb_validate_urb(struct urb *urb)
+{
+	if (!urb) {
+		mausb_pr_err("urb is NULL");
+		return -EINVAL;
+	}
+
+	if (!urb->ep->hcpriv) {
+		mausb_pr_err("urb->ep->hcpriv is NULL");
+		return -EINVAL;
+	}
+
+	if (!urb->ep->enabled) {
+		mausb_pr_err("Endpoint not enabled");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				 gfp_t mem_flags)
+{
+	struct mausb_endpoint_ctx *endpoint_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_urb_ctx	  *urb_ctx;
+	int status = 0;
+
+	if (mausb_validate_urb(urb) < 0) {
+		mausb_pr_err("Hpal urb enqueue failed");
+		return -EPROTO;
+	}
+
+	endpoint_ctx = urb->ep->hcpriv;
+	ma_dev = endpoint_ctx->ma_dev;
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - finish urb immediately");
+		return -EHOSTDOWN;
+	}
+
+	urb->hcpriv = hcd;
+
+	mausb_pr_debug("ep_handle=%#x, dev_handle=%#x, urb_reject=%d",
+		       endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		       atomic_read(&urb->reject));
+
+	status = mausb_insert_urb_in_tree(urb, true);
+	if (status) {
+		mausb_pr_err("Hpal urb enqueue failed");
+		return status;
+	}
+
+	atomic_inc(&urb->use_count);
+
+	mausb_print_urb(urb);
+
+	/*
+	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
+	 * should not, so it is breaking the USB drive on the linux
+	 */
+	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
+
+	status = mausb_data_req_enqueue_event(ma_dev, endpoint_ctx->ep_handle,
+					      urb);
+	if (status < 0) {
+		urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+		atomic_dec(&urb->use_count);
+		if (urb_ctx) {
+			mausb_uninit_data_iterator(&urb_ctx->iterator);
+			kfree(urb_ctx);
+		}
+	}
+
+	return status;
+}
+
+static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				 int status)
+{
+	struct mausb_endpoint_ctx *endpoint_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_urb_ctx	  *urb_ctx;
+
+	mausb_pr_info("Urb=%p", urb);
+
+	urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+	if (!urb_ctx) {
+		mausb_pr_warn("Urb=%p is not in tree", urb);
+		return 0;
+	}
+
+	endpoint_ctx = urb->ep->hcpriv;
+	ma_dev	     = endpoint_ctx->ma_dev;
+
+	queue_work(ma_dev->workq, &urb_ctx->work);
+
+	return 0;
+}
+
+void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status)
+{
+	struct mausb_urb_ctx *urb_ctx =
+		mausb_unlink_and_delete_urb_from_tree(urb, status);
+
+	if (urb_ctx) {
+		mausb_uninit_data_iterator(&urb_ctx->iterator);
+		kfree(urb_ctx);
+
+		urb->status	   = status;
+		urb->actual_length = actual_length;
+
+		atomic_dec(&urb->use_count);
+		usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+		return;
+	}
+}
+
 int mausb_probe(struct device *dev)
 {
 	struct mausb_hcd *mausb_hcd;
@@ -765,8 +1033,10 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 	u8	port_number;
 	s16	dev_handle;
 	int	status;
+	unsigned long	 flags;
 	struct hub_ctx   *hub  = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_dev	    *mdev = NULL;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct mausb_endpoint_ctx   *ep_ctx = dev->ep0.hcpriv;
 
@@ -779,6 +1049,16 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 
 	mdev  = &hub->ma_devs[port_number];
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(mdev, dev);
 	if (!usb_device_ctx) {
 		mausb_pr_warn("device_ctx is not found");
@@ -787,6 +1067,13 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 
 	dev_handle = usb_device_ctx->dev_handle;
 
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - free usbdevice immediately");
+		dev->ep0.hcpriv = NULL;
+		kfree(ep_ctx);
+		goto free_dev;
+	}
+
 	if (ep_ctx) {
 		dev->ep0.hcpriv = NULL;
 		kfree(ep_ctx);
@@ -795,9 +1082,51 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle);
 	}
 
+free_dev:
+	if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
+		mausb_pr_info("All usb devices destroyed - proceed with disconnecting");
+		queue_work(ma_dev->workq, &ma_dev->socket_disconnect_work);
+	}
+
 	rb_erase(&usb_device_ctx->rb_node, &mdev->usb_devices);
 	mausb_pr_info("USB device deleted device=%p", usb_device_ctx->dev_addr);
 	kfree(usb_device_ctx);
+
+	if (kref_put(&ma_dev->refcount, mausb_release_ma_dev_async))
+		mausb_clear_hcd_madev(port_number);
+}
+
+static struct mausb_usb_device_ctx *
+mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
+		       struct mausb_device *ma_dev, u16 port_number,
+		       int *status)
+{
+	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+
+	usb_device_ctx = kzalloc(sizeof(*usb_device_ctx), GFP_ATOMIC);
+	if (!usb_device_ctx) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	usb_device_ctx->dev_addr   = dev;
+	usb_device_ctx->dev_handle = DEV_HANDLE_NOT_ASSIGNED;
+	usb_device_ctx->addressed  = false;
+
+	if (mausb_insert_usb_device(&hub->ma_devs[port_number],
+				    usb_device_ctx)) {
+		mausb_pr_warn("device_ctx already exists");
+		kfree(usb_device_ctx);
+		*status = -EEXIST;
+		return NULL;
+	}
+
+	kref_get(&ma_dev->refcount);
+	mausb_pr_info("New USB device added device=%p",
+		      usb_device_ctx->dev_addr);
+	atomic_inc(&ma_dev->num_of_usb_devices);
+
+	return usb_device_ctx;
 }
 
 /*
@@ -808,7 +1137,9 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 {
 	u8	port_number;
 	int	status;
+	unsigned long	flags;
 	struct hub_ctx	*hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx;
 
@@ -819,9 +1150,23 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
-	if (!usb_device_ctx)
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_warn("MAUSB device not found on port_number=%d",
+			      port_number);
 		return -ENODEV;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
+							port_number, &status);
+		if (!usb_device_ctx)
+			return status;
+	}
 
 	mausb_pr_info("dev_handle=%#x, dev_speed=%#x",
 		      usb_device_ctx->dev_handle, dev->speed);
@@ -852,9 +1197,13 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	int	status;
 	u8	port_number;
+	struct ma_usb_ephandlereq_desc_ss  descriptor_ss;
+	struct ma_usb_ephandlereq_desc_std descriptor;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx;
+	unsigned long flags = 0;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -863,6 +1212,16 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return 0;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!usb_dev_ctx) {
@@ -876,8 +1235,17 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 
 	endpoint_ctx->dev_handle	= usb_dev_ctx->dev_handle;
 	endpoint_ctx->usb_device_ctx	= usb_dev_ctx;
+	endpoint_ctx->ma_dev		= ma_dev;
 	endpoint->hcpriv		= endpoint_ctx;
 
+	if (dev->speed >= USB_SPEED_SUPER) {
+		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
+						    &endpoint->desc,
+						    &endpoint->ss_ep_comp);
+	} else {
+		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+	}
+
 	return 0;
 }
 
@@ -887,8 +1255,10 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 	u8	port_number;
 	int	status;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx = endpoint->hcpriv;
+	unsigned long flags = 0;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -897,6 +1267,16 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!endpoint_ctx) {
@@ -913,6 +1293,70 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 	return 0;
 }
 
+static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
+					  struct usb_device *dev,
+					  struct hub_ctx *hub,
+					  struct mausb_device *ma_dev,
+					  struct mausb_usb_device_ctx
+					  *usb_device_ctx)
+{
+	u8 port_number;
+	int status;
+	int dev_speed;
+	u16 hub_dev_handle		= 0;
+	u16 parent_hs_hub_dev_handle	= 0;
+	u16 parent_hs_hub_port		= 0;
+	struct usb_device		   *first_hub_device = dev;
+	struct mausb_usb_device_ctx	   *hub_device_ctx;
+	struct mausb_endpoint_ctx	   *endpoint_ctx;
+	struct ma_usb_ephandlereq_desc_std descriptor;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	while (first_hub_device->parent->parent)
+		first_hub_device = first_hub_device->parent;
+
+	hub_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
+					       first_hub_device);
+	if (hub_device_ctx)
+		hub_dev_handle = hub_device_ctx->dev_handle;
+
+	if ((dev->speed == USB_SPEED_LOW || dev->speed == USB_SPEED_FULL) &&
+	    first_hub_device->speed == USB_SPEED_HIGH) {
+		parent_hs_hub_dev_handle =
+			mausb_find_usb_device(&hub->ma_devs[port_number],
+					      dev->parent)->dev_handle;
+		parent_hs_hub_port = dev->parent->portnum;
+	}
+
+	dev_speed = usb_to_mausb_device_speed(dev->speed);
+	mausb_pr_info("start... mausb_devspeed=%d, route=%#x, port_number=%d",
+		      dev_speed, dev->route, port_number);
+
+	if (dev_speed == -EINVAL) {
+		mausb_pr_err("bad dev_speed");
+		return -EINVAL;
+	}
+
+	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
+	if (!endpoint_ctx)
+		return -ENOMEM;
+
+	endpoint_ctx->dev_handle     = usb_device_ctx->dev_handle;
+	endpoint_ctx->ma_dev	     = ma_dev;
+	endpoint_ctx->usb_device_ctx = usb_device_ctx;
+	dev->ep0.hcpriv		     = endpoint_ctx;
+
+	mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
+
+	return 0;
+}
+
 /*
  * For usb 2.0 logitech camera called multiple times, once during enumeration
  * of device and later after mausb_reset_device. In latter case it is
@@ -923,7 +1367,9 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 	int status;
 	u8 port_number;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
+	unsigned long flags;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -932,13 +1378,27 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
-	if (!usb_device_ctx)
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
 		return -ENODEV;
+	}
 
-	mausb_pr_info("Device assigned and addressed usb_device_ctx=%p",
-		      usb_device_ctx);
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
+							port_number, &status);
+		if (!usb_device_ctx)
+			return status;
+	}
 
+	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
+		return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
+						      usb_device_ctx);
 	return 0;
 }
 
@@ -952,7 +1412,9 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 	u8	port_number = 0;
 	int	status	    = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev = NULL;
 	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+	unsigned long flags = 0;
 
 	if (mausb_is_hub_device(dev)) {
 		mausb_pr_warn("Device is hub");
@@ -966,6 +1428,16 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 	if (!usb_device_ctx) {
 		mausb_pr_warn("Device not found");
@@ -980,10 +1452,12 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	int	status;
 	u8	port_number;
+	unsigned long flags;
 	u16 max_exit_latency = 0;
 	u8  mtt = 0;
 	u8  ttt = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 
 	if (dev->speed == USB_SPEED_HIGH) {
@@ -998,6 +1472,16 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 		return 0;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
 					       dev);
 
@@ -1038,10 +1522,12 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	int is_control;
 	int epnum;
 	int is_out;
+	unsigned long flags;
 	u16	dev_handle;
 	u8	tsp;
 	u8	port_number;
 	struct hub_ctx		    *hub;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct usb_device	    *dev;
 	struct mausb_endpoint_ctx   *ep_ctx;
@@ -1064,14 +1550,21 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	}
 	hub = (struct hub_ctx *)hcd->hcd_priv;
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return;
+	}
+
 	is_control = usb_endpoint_xfer_control(&endpoint->desc);
 	epnum = usb_endpoint_num(&endpoint->desc);
 	is_out = usb_endpoint_dir_out(&endpoint->desc);
 	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
 
-	if (status < 0)
-		return;
-
 	if (status != EUCLEAN) {
 		if (!tsp) {
 			usb_settoggle(dev, epnum, is_out, 0U);
@@ -1099,7 +1592,9 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 	int status;
 	u8  port_number;
 	u16 dev_handle;
+	unsigned long flags;
 	struct hub_ctx		    *hub;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 
 	hub = (struct hub_ctx *)hcd->hcd_priv;
@@ -1111,6 +1606,16 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!usb_device_ctx ||
diff --git a/drivers/usb/mausb_host/hcd.h b/drivers/usb/mausb_host/hcd.h
index cbef70a2f985..d4e9267503d1 100644
--- a/drivers/usb/mausb_host/hcd.h
+++ b/drivers/usb/mausb_host/hcd.h
@@ -15,6 +15,8 @@
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 
+#include "hpal.h"
+
 #define DEVICE_NAME "mausb_host_hcd_dev"
 #define CLASS_NAME "mausb"
 
@@ -65,6 +67,13 @@ struct hub_ctx {
 int mausb_init_hcd(void);
 void mausb_deinit_hcd(void);
 
+void mausb_port_has_changed(const enum mausb_device_type device_type,
+			    const enum mausb_device_speed device_speed,
+			    void *ma_dev);
+void mausb_hcd_disconnect(const u16 port_number,
+			  const enum mausb_device_type device_type,
+			  const enum mausb_device_speed device_speed);
+
 #define PORT_C_MASK \
 		((USB_PORT_STAT_C_CONNECTION \
 		| USB_PORT_STAT_C_ENABLE \
@@ -140,11 +149,13 @@ struct mausb_endpoint_ctx {
 
 struct mausb_urb_ctx {
 	struct urb		*urb;
+	struct mausb_data_iter	iterator;
 	struct rb_node		rb_node;
 	struct work_struct	work;
 };
 
 int mausb_probe(struct device *dev);
+void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status);
 
 void mausb_clear_hcd_madev(u16 port_number);
 
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
new file mode 100644
index 000000000000..ac86cbc71e36
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal.c
@@ -0,0 +1,1094 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal.h"
+
+#include <linux/circ_buf.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/uio.h>
+
+#include "hcd.h"
+#include "utils.h"
+
+struct mss mss;
+
+static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
+				void *data);
+static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
+static int mausb_start_heartbeat_timer(void);
+
+static inline struct mausb_urb_ctx *__mausb_find_urb_in_tree(struct urb *urb)
+{
+	struct rb_node *node = mhcd->mausb_urbs.rb_node;
+
+	while (node) {
+		struct mausb_urb_ctx *urb_ctx =
+		    rb_entry(node, struct mausb_urb_ctx, rb_node);
+
+		if (urb < urb_ctx->urb)
+			node = urb_ctx->rb_node.rb_left;
+		else if (urb > urb_ctx->urb)
+			node = urb_ctx->rb_node.rb_right;
+		else
+			return urb_ctx;
+	}
+	return NULL;
+}
+
+struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb)
+{
+	unsigned long flags = 0;
+	struct mausb_urb_ctx *urb_ctx;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	urb_ctx =  __mausb_find_urb_in_tree(urb);
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return urb_ctx;
+}
+
+static int mausb_insert_urb_ctx_in_tree(struct mausb_urb_ctx *urb_ctx)
+{
+	struct rb_node **new_node = &mhcd->mausb_urbs.rb_node;
+	struct rb_node *parent = NULL;
+	struct mausb_urb_ctx *current_urb = NULL;
+
+	while (*new_node) {
+		parent = *new_node;
+		current_urb = rb_entry(*new_node, struct mausb_urb_ctx,
+				       rb_node);
+
+		if (urb_ctx->urb < current_urb->urb)
+			new_node = &((*new_node)->rb_left);
+		else if (urb_ctx->urb > current_urb->urb)
+			new_node = &((*new_node)->rb_right);
+		else
+			return -EEXIST;
+	}
+	rb_link_node(&urb_ctx->rb_node, parent, new_node);
+	rb_insert_color(&urb_ctx->rb_node, &mhcd->mausb_urbs);
+	return 0;
+}
+
+static void mausb_delete_urb_ctx_from_tree(struct mausb_urb_ctx *urb_ctx)
+{
+	rb_erase(&urb_ctx->rb_node, &mhcd->mausb_urbs);
+}
+
+static struct mausb_urb_ctx *mausb_create_urb_ctx(struct urb *urb, int *status)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+
+	if (!urb) {
+		mausb_pr_err("Urb is NULL");
+		*status = -EINVAL;
+		return NULL;
+	}
+
+	urb_ctx = kzalloc(sizeof(*urb_ctx), GFP_ATOMIC);
+	if (!urb_ctx) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	urb_ctx->urb = urb;
+	INIT_WORK(&urb_ctx->work, mausb_execute_urb_dequeue);
+
+	return urb_ctx;
+}
+
+int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep)
+{
+	unsigned long flags;
+	int status = 0;
+
+	struct mausb_urb_ctx *urb_ctx = mausb_create_urb_ctx(urb, &status);
+
+	if (!urb_ctx)
+		return status;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (link_urb_to_ep) {
+		status = usb_hcd_link_urb_to_ep(urb->hcpriv, urb);
+		if (status) {
+			spin_unlock_irqrestore(&mhcd->lock, flags);
+			mausb_pr_err("Error %d while linking urb to hcd_endpoint",
+				     status);
+			kfree(urb_ctx);
+			return status;
+		}
+	}
+
+	if (mausb_insert_urb_ctx_in_tree(urb_ctx)) {
+		kfree(urb_ctx);
+		if (link_urb_to_ep)
+			usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		mausb_pr_err("Urb_ctx insertion failed");
+		return -EEXIST;
+	}
+
+	mausb_init_data_iterator(&urb_ctx->iterator, urb->transfer_buffer,
+				 urb->transfer_buffer_length, urb->sg,
+				 (unsigned int)urb->num_sgs,
+				 usb_urb_dir_in(urb));
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx,
+					 bool link_urb_to_ep)
+{
+	unsigned long flags;
+	int status;
+
+	if (!urb_ctx)
+		return false;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (link_urb_to_ep) {
+		status = usb_hcd_link_urb_to_ep(urb_ctx->urb->hcpriv,
+						urb_ctx->urb);
+		if (status) {
+			spin_unlock_irqrestore(&mhcd->lock, flags);
+			mausb_pr_err("Error %d while linking urb to hcd_endpoint",
+				     status);
+			return false;
+		}
+	}
+
+	if (mausb_insert_urb_ctx_in_tree(urb_ctx)) {
+		if (link_urb_to_ep)
+			usb_hcd_unlink_urb_from_ep(urb_ctx->urb->hcpriv,
+						   urb_ctx->urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		mausb_pr_err("Urb_ctx insertion failed");
+		return false;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return true;
+}
+
+/*After this function call only valid thing to do with urb is to give it back*/
+struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
+							    int status)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+	unsigned long flags;
+	int ret;
+
+	if (!urb) {
+		mausb_pr_warn("Urb is NULL");
+		return NULL;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	urb_ctx = __mausb_find_urb_in_tree(urb);
+
+	if (!urb_ctx) {
+		mausb_pr_warn("Urb=%p not in tree", urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return NULL;
+	}
+
+	ret = usb_hcd_check_unlink_urb(urb->hcpriv, urb, status);
+
+	if (ret == -EIDRM)
+		mausb_pr_warn("Urb=%p is already unlinked", urb);
+	else
+		usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb);
+
+	mausb_delete_urb_ctx_from_tree(urb_ctx);
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	mausb_pr_debug("Urb=%p is removed from tree", urb);
+
+	return urb_ctx;
+}
+
+void mausb_release_event_resources(struct mausb_event *event)
+{
+	struct ma_usb_hdr_common *receive_buffer = (struct ma_usb_hdr_common *)
+						    event->data.recv_buf;
+
+	kfree(receive_buffer);
+}
+
+void mausb_complete_urb(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size,
+		       event->data.rem_transfer_size, event->status);
+	mausb_complete_request(urb,
+			       event->data.transfer_size -
+			       event->data.rem_transfer_size,
+			       event->status);
+}
+
+static int mausb_get_first_free_port_number(u16 *port_number)
+{
+	(*port_number) = 0;
+	while ((mhcd->connected_ports & (1 << *port_number)) != 0 &&
+	       *port_number < NUMBER_OF_PORTS)
+		++(*port_number);
+
+	if (*port_number == NUMBER_OF_PORTS)
+		return -EINVAL;
+
+	mhcd->connected_ports |= (1 << *port_number);
+
+	return 0;
+}
+
+static inline void mausb_port_has_changed_event(struct mausb_device *dev,
+						struct mausb_event *event)
+{
+	int status;
+	u16 port_number;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	status = mausb_get_first_free_port_number(&port_number);
+	if (status < 0) {
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		mausb_pr_err("There is no free port, schedule delete ma_dev");
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	dev->dev_type	   = event->port_changed.dev_type;
+	dev->dev_speed	   = event->port_changed.dev_speed;
+	dev->lse	   = event->port_changed.lse;
+	dev->dev_connected = 1;
+	dev->port_number   = port_number;
+
+	mausb_port_has_changed(event->port_changed.dev_type,
+			       event->port_changed.dev_speed, dev);
+
+	if ((enum mausb_device_type)event->port_changed.dev_type == USB30HUB)
+		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
+}
+
+static void mausb_heartbeat_timer_func(struct timer_list *timer)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	if (mausb_start_heartbeat_timer() < 0) {
+		mausb_pr_err("Devices disconnecting - app is unresponsive");
+		spin_lock_irqsave(&mss.lock, flags);
+
+		/* Reset connected clients */
+		mss.client_connected = false;
+		mss.missed_heartbeats = 0;
+
+		list_for_each_entry(dev, &mss.madev_list, list_entry) {
+			mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x",
+				       dev->madev_addr);
+			queue_work(dev->workq, &dev->heartbeat_work);
+		}
+
+		complete(&mss.client_stopped);
+		spin_unlock_irqrestore(&mss.lock, flags);
+	}
+}
+
+void mausb_release_ma_dev_async(struct kref *kref)
+{
+	struct mausb_device *dev = container_of(kref, struct mausb_device,
+						refcount);
+
+	mausb_pr_info("Scheduling work for MAUSB device to be deleted");
+
+	schedule_work(&dev->madev_delete_work);
+}
+
+int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
+				  u16 num_of_completed)
+{
+	unsigned long flags;
+	struct mausb_device *dev;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(madev_addr);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -EINVAL;
+	}
+
+	spin_lock(&dev->num_of_user_events_lock);
+	dev->num_of_user_events += num_of_events;
+	dev->num_of_completed_events += num_of_completed;
+	spin_unlock(&dev->num_of_user_events_lock);
+	queue_work(dev->workq, &dev->work);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+}
+
+int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
+				 struct urb *request)
+{
+	struct mausb_event mausb_event;
+
+	mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
+	mausb_event.status = 0;
+
+	mausb_event.data.transfer_type =
+		mausb_transfer_type_from_usb(&request->ep->desc);
+	mausb_event.data.device_id	= dev->id;
+	mausb_event.data.ep_handle	= ep_handle;
+	mausb_event.data.urb		= (u64)request;
+	mausb_event.data.setup_packet	=
+		(usb_endpoint_xfer_control(&request->ep->desc) &&
+			request->setup_packet);
+	mausb_event.data.transfer_size	= request->transfer_buffer_length;
+	mausb_event.data.direction	= (usb_urb_dir_in(request) ?
+						MAUSB_DATA_MSG_DIRECTION_IN :
+						MAUSB_DATA_MSG_DIRECTION_OUT);
+	mausb_event.data.transfer_size +=
+		((mausb_event.data.direction == MAUSB_DATA_MSG_DIRECTION_OUT &&
+			mausb_event.data.setup_packet) ?
+				MAUSB_CONTROL_SETUP_SIZE : 0);
+	mausb_event.data.rem_transfer_size = mausb_event.data.transfer_size;
+	mausb_event.data.transfer_flags	   = request->transfer_flags;
+	mausb_event.data.transfer_eot	   = false;
+	mausb_event.data.isoch_seg_num	   = (u32)request->number_of_packets;
+	mausb_event.data.recv_buf	   = 0;
+	mausb_event.data.payload_size	   =
+		(usb_endpoint_xfer_isoc(&request->ep->desc) &&
+		 usb_endpoint_dir_out(&request->ep->desc)) ?
+		(request->iso_frame_desc[request->number_of_packets - 1]
+								.offset +
+		 request->iso_frame_desc[request->number_of_packets - 1]
+								.length) : 0;
+
+	if (mausb_event.data.setup_packet) {
+		memcpy(mausb_event.data.hdr_ack, request->setup_packet,
+		       MAUSB_CONTROL_SETUP_SIZE);
+		memcpy(shift_ptr(mausb_event.data.hdr_ack,
+				 MAUSB_CONTROL_SETUP_SIZE),
+		       &request->dev->route, sizeof(request->dev->route));
+	}
+
+	return 0;
+}
+
+void mausb_complete_request(struct urb *urb, u32 actual_length, int status)
+{
+	mausb_hcd_urb_complete(urb, actual_length, status);
+}
+
+int mausb_signal_event(struct mausb_device *dev,
+		       struct mausb_event *event, u64 event_id)
+{
+	unsigned long flags;
+	struct mausb_completion *mausb_completion;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_for_each_entry(mausb_completion, &dev->completion_events,
+			    list_entry) {
+		if (mausb_completion->event_id == event_id) {
+			memcpy(mausb_completion->mausb_event, event,
+			       sizeof(*event));
+			complete(mausb_completion->completion_event);
+			spin_unlock_irqrestore(&dev->completion_events_lock,
+					       flags);
+			return 0;
+		}
+	}
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+
+	return -ETIMEDOUT;
+}
+
+void mausb_reset_connection_timer(struct mausb_device *dev)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&dev->connection_timer_lock, flags);
+	dev->receive_failures_num = 0;
+
+	mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+	spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+}
+
+static int mausb_start_heartbeat_timer(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (++mss.missed_heartbeats > MAUSB_MAX_MISSED_HEARTBEATS) {
+		mausb_pr_err("Missed more than %d heartbeats",
+			     MAUSB_MAX_MISSED_HEARTBEATS);
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+	mod_timer(&mss.heartbeat_timer,
+		  jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
+	return 0;
+}
+
+void mausb_reset_heartbeat_cnt(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	mss.missed_heartbeats = 0;
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
+{
+	struct mausb_urb_ctx *urb_ctx =
+			container_of(dequeue_work, struct mausb_urb_ctx, work);
+	struct urb		  *urb = urb_ctx->urb;
+	struct mausb_endpoint_ctx *ep_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_event	  mausb_event;
+	int status = 0;
+
+	ep_ctx = urb->ep->hcpriv;
+	ma_dev = ep_ctx->ma_dev;
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - finish urb immediately urb=%p, ep_handle=%#x, dev_handle=%#x",
+			     urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
+		goto complete_urb;
+	}
+
+	mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x",
+		       urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
+
+	memset(&mausb_event, 0, sizeof(mausb_event));
+
+	mausb_event.type   = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER;
+	mausb_event.status = 0;
+
+	mausb_event.data.transfer_type =
+				mausb_transfer_type_from_usb(&urb->ep->desc);
+	mausb_event.data.device_id     = ma_dev->id;
+	mausb_event.data.ep_handle     = ep_ctx->ep_handle;
+	mausb_event.data.urb	       = (u64)urb;
+	mausb_event.data.direction     = (usb_urb_dir_in(urb) ?
+						MAUSB_DATA_MSG_DIRECTION_IN :
+						MAUSB_DATA_MSG_DIRECTION_OUT);
+
+	if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) {
+		mausb_pr_alert("Failed to insert in tree urb=%p ep_handle=%#x, status=%d",
+			       urb, mausb_event.data.ep_handle, status);
+		goto complete_urb;
+	}
+
+	return;
+
+complete_urb:
+
+	/* Deallocate urb_ctx */
+	mausb_uninit_data_iterator(&urb_ctx->iterator);
+	kfree(urb_ctx);
+
+	urb->status	   = -EPROTO;
+	urb->actual_length = 0;
+	atomic_dec(&urb->use_count);
+	usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+}
+
+void mausb_initialize_mss(void)
+{
+	spin_lock_init(&mss.lock);
+	INIT_LIST_HEAD(&mss.madev_list);
+	INIT_LIST_HEAD(&mss.available_ring_buffers);
+
+	init_completion(&mss.empty);
+	complete(&mss.empty);
+	init_completion(&mss.rings_events.mausb_ring_has_events);
+	atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 0);
+	mss.deinit_in_progress	= false;
+	mss.ring_buffer_id	= 0;
+	mss.client_connected = false;
+	mss.missed_heartbeats = 0;
+	init_completion(&mss.client_stopped);
+	atomic_set(&mss.num_of_transitions_to_sleep, 0);
+
+	timer_setup(&mss.heartbeat_timer, mausb_heartbeat_timer_func, 0);
+}
+
+void mausb_deinitialize_mss(void)
+{
+	struct mausb_device *dev = NULL;
+	unsigned long flags = 0;
+	unsigned long timeout =
+			msecs_to_jiffies(MAUSB_CLIENT_STOPPED_TIMEOUT_MS);
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	mss.deinit_in_progress = true;
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mausb_pr_debug("Enqueue mausb_hcd_disconnect_work madev_addr=%x",
+			       dev->madev_addr);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	wait_for_completion(&mss.empty);
+	mausb_pr_debug("Waiting for completion on disconnect_event ended");
+
+	timeout = wait_for_completion_timeout(&mss.client_stopped, timeout);
+	mausb_pr_info("Remaining time after waiting for stopping client %ld",
+		      timeout);
+}
+
+int mausb_register_power_state_listener(void)
+{
+	mausb_pr_info("Registering power states listener");
+
+	mhcd->power_state_listener.notifier_call = mausb_power_state_cb;
+	return register_pm_notifier(&mhcd->power_state_listener);
+}
+
+void mausb_unregister_power_state_listener(void)
+{
+	mausb_pr_info("Un-registering power states listener");
+
+	unregister_pm_notifier(&mhcd->power_state_listener);
+}
+
+static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
+				void *data)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	mausb_pr_info("Power state callback action = %ld", action);
+	if (action == PM_SUSPEND_PREPARE || action == PM_HIBERNATION_PREPARE) {
+		/* Stop heartbeat timer */
+		del_timer_sync(&mss.heartbeat_timer);
+		mausb_pr_info("Saving state before sleep");
+		spin_lock_irqsave(&mss.lock, flags);
+		if (!list_empty(&mss.madev_list))
+			atomic_inc(&mss.num_of_transitions_to_sleep);
+
+		list_for_each_entry(dev, &mss.madev_list, list_entry) {
+			mausb_pr_info("Enqueue heartbeat_work madev_addr=%x",
+				      dev->madev_addr);
+			queue_work(dev->workq, &dev->heartbeat_work);
+		}
+
+		spin_unlock_irqrestore(&mss.lock, flags);
+	} else if (action == PM_POST_SUSPEND || action == PM_POST_HIBERNATION) {
+		mausb_reset_heartbeat_cnt();
+		/* Start hearbeat timer */
+		mod_timer(&mss.heartbeat_timer, jiffies +
+			  msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+	}
+	return NOTIFY_OK;
+}
+
+static void mausb_populate_standard_ep_descriptor(struct usb_ep_desc *std_desc,
+						  struct usb_endpoint_descriptor
+						  *usb_std_desc)
+{
+	std_desc->bLength	   = usb_std_desc->bLength;
+	std_desc->bDescriptorType  = usb_std_desc->bDescriptorType;
+	std_desc->bEndpointAddress = usb_std_desc->bEndpointAddress;
+	std_desc->bmAttributes	   = usb_std_desc->bmAttributes;
+	std_desc->wMaxPacketSize   = usb_std_desc->wMaxPacketSize;
+	std_desc->bInterval	   = usb_std_desc->bInterval;
+}
+
+static void
+mausb_populate_superspeed_ep_descriptor(struct usb_ss_ep_comp_desc *ss_desc,
+					struct usb_ss_ep_comp_descriptor*
+					usb_ss_desc)
+{
+	ss_desc->bLength	   = usb_ss_desc->bLength;
+	ss_desc->bDescriptorType   = usb_ss_desc->bDescriptorType;
+	ss_desc->bMaxBurst	   = usb_ss_desc->bMaxBurst;
+	ss_desc->bmAttributes	   = usb_ss_desc->bmAttributes;
+	ss_desc->wBytesPerInterval = usb_ss_desc->wBytesPerInterval;
+}
+
+void
+mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *std_desc,
+				  struct usb_endpoint_descriptor *usb_std_desc)
+{
+	mausb_populate_standard_ep_descriptor(&std_desc->usb20, usb_std_desc);
+}
+
+void
+mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *ss_desc,
+				    struct usb_endpoint_descriptor *
+				    usb_std_desc,
+				    struct usb_ss_ep_comp_descriptor *
+				    usb_ss_desc)
+{
+	mausb_populate_standard_ep_descriptor(&ss_desc->usb20, usb_std_desc);
+	mausb_populate_superspeed_ep_descriptor(&ss_desc->usb31, usb_ss_desc);
+}
+
+struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr)
+{
+	struct mausb_device *dev = NULL;
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		if (dev->madev_addr == madev_addr)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static inline
+struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
+					    enum mausb_channel channel)
+{
+	if (channel >= MAUSB_CHANNEL_MAP_LENGTH)
+		return NULL;
+
+	return ma_dev->channel_map[channel];
+}
+
+int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num,
+		    struct mausb_kvec_data_wrapper *data)
+{
+	struct mausb_ip_ctx *channel = mausb_get_data_channel(dev, channel_num);
+	int status = 0;
+
+	if (!channel)
+		return -ECHRNG;
+
+	status = mausb_ip_send(channel, data);
+
+	if (status < 0) {
+		mausb_pr_err("Send failed. Disconnecting... status=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	return status;
+}
+
+int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct ma_usb_hdr_common *ack_hdr;
+	struct kvec kvec;
+	struct mausb_kvec_data_wrapper data_to_send;
+	enum mausb_channel channel;
+
+	ack_hdr = (struct ma_usb_hdr_common *)(&event->data.hdr_ack);
+
+	data_to_send.kvec	    = &kvec;
+	data_to_send.kvec->iov_base = ack_hdr;
+	data_to_send.kvec->iov_len  = ack_hdr->length;
+	data_to_send.kvec_num	    = 1;
+	data_to_send.length	    = ack_hdr->length;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
+
+	if (event->status != 0) {
+		mausb_pr_err("Event %d failed with status %d",
+			     event->type, event->status);
+		mausb_complete_urb(event);
+		return event->status;
+	}
+
+	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
+
+	if (!urb_ctx) {
+		/* Transfer will be deleted from dequeue task */
+		mausb_pr_warn("Urb is already cancelled for event=%d",
+			      event->type);
+		return status;
+	}
+
+	return status;
+}
+
+int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	int status = 0;
+	struct mausb_urb_ctx *urb_ctx;
+
+	mausb_pr_debug("Direction=%d", event->data.direction);
+
+	if (!mausb_isoch_data_event(event)) {
+		status = mausb_send_transfer_ack(dev, event);
+		if (status < 0) {
+			mausb_pr_warn("Sending acknowledgment failed");
+			goto cleanup;
+		}
+	}
+
+	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
+
+	if (!urb_ctx)
+		mausb_pr_warn("Urb is already cancelled");
+
+cleanup:
+	mausb_release_event_resources(event);
+	return status;
+}
+
+int mausb_add_data_chunk(void *buffer, u32 buffer_size,
+			 struct list_head *chunks_list)
+{
+	struct mausb_payload_chunk *data_chunk;
+
+	data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+	if (!data_chunk)
+		return -ENOMEM;
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	INIT_LIST_HEAD(&data_chunk->list_entry);
+
+	data_chunk->kvec.iov_base = buffer;
+	data_chunk->kvec.iov_len  = buffer_size;
+	list_add_tail(&data_chunk->list_entry, chunks_list);
+	return 0;
+}
+
+int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data,
+			    struct list_head *chunks_list,
+			    u32 num_of_data_chunks)
+{
+	struct mausb_payload_chunk *data_chunk = NULL;
+	struct mausb_payload_chunk *tmp = NULL;
+	u32 current_kvec = 0;
+
+	data->length = 0;
+	data->kvec = kcalloc(num_of_data_chunks, sizeof(struct kvec),
+			     GFP_KERNEL);
+	if (!data->kvec)
+		return -ENOMEM;
+
+	list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) {
+		data->kvec[current_kvec].iov_base =
+			data_chunk->kvec.iov_base;
+		data->kvec[current_kvec].iov_len =
+		    data_chunk->kvec.iov_len;
+		++data->kvec_num;
+		data->length += data_chunk->kvec.iov_len;
+		++current_kvec;
+	}
+	return 0;
+}
+
+void mausb_cleanup_chunks_list(struct list_head *chunks_list)
+{
+	struct mausb_payload_chunk *data_chunk = NULL;
+	struct mausb_payload_chunk *tmp = NULL;
+
+	list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) {
+		list_del(&data_chunk->list_entry);
+		kfree(data_chunk);
+	}
+}
+
+static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
+				     u32 byte_num,
+				     struct list_head *data_chunks_list,
+				     u32 *data_chunks_num)
+{
+	u32 rem_data		= 0;
+	u32 bytes_to_read	= 0;
+	struct mausb_payload_chunk *data_chunk = NULL;
+
+	(*data_chunks_num) = 0;
+
+	if (!data_chunks_list)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(data_chunks_list);
+	rem_data      = iterator->length - iterator->offset;
+	bytes_to_read = min(byte_num, rem_data);
+
+	if (bytes_to_read == 0)
+		return 0;
+
+	data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+
+	if (!data_chunk)
+		return -ENOMEM;
+
+	++(*data_chunks_num);
+
+	data_chunk->kvec.iov_base = (u8 *)(iterator->buffer) + iterator->offset;
+	data_chunk->kvec.iov_len = bytes_to_read;
+	iterator->offset += bytes_to_read;
+
+	list_add_tail(&data_chunk->list_entry, data_chunks_list);
+
+	return 0;
+}
+
+static int mausb_read_scatterlist_buffer(struct mausb_data_iter *iterator,
+					 u32 byte_num,
+					 struct list_head *data_chunks_list,
+					 u32 *data_chunks_num)
+{
+	u32 current_sg_read_num;
+	struct mausb_payload_chunk *data_chunk = NULL;
+
+	(*data_chunks_num) = 0;
+
+	if (!data_chunks_list)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(data_chunks_list);
+
+	while (byte_num) {
+		if (iterator->sg_iter.consumed == iterator->sg_iter.length) {
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		}
+
+		data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+		if (!data_chunk) {
+			sg_miter_stop(&iterator->sg_iter);
+			return -ENOMEM;
+		}
+
+		current_sg_read_num = min((size_t)byte_num,
+					  iterator->sg_iter.length -
+					  iterator->sg_iter.consumed);
+
+		data_chunk->kvec.iov_base = (u8 *)iterator->sg_iter.addr +
+				iterator->sg_iter.consumed;
+		data_chunk->kvec.iov_len  = current_sg_read_num;
+
+		++(*data_chunks_num);
+		list_add_tail(&data_chunk->list_entry, data_chunks_list);
+
+		byte_num -= current_sg_read_num;
+		iterator->sg_iter.consumed += current_sg_read_num;
+		data_chunk = NULL;
+	}
+
+	return 0;
+}
+
+static u32 mausb_write_virtual_buffer(struct mausb_data_iter *iterator,
+				      void *buffer, u32 size)
+{
+	u32 rem_space   = 0;
+	u32 write_count = 0;
+
+	if (!buffer || !size)
+		return write_count;
+
+	rem_space   = iterator->length - iterator->offset;
+	write_count = min(size, rem_space);
+
+	if (write_count > 0) {
+		void *location = shift_ptr(iterator->buffer, iterator->offset);
+
+		memcpy(location, buffer, write_count);
+		iterator->offset += write_count;
+	}
+
+	return write_count;
+}
+
+static u32 mausb_write_scatterlist_buffer(struct mausb_data_iter *iterator,
+					  void *buffer, u32 size)
+{
+	u32 current_sg_rem_space;
+	u32 count = 0;
+	u32 total_count = 0;
+	void *location = NULL;
+
+	if (!buffer || !size)
+		return count;
+
+	while (size) {
+		if (iterator->sg_iter.consumed >= iterator->sg_iter.length) {
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		}
+
+		current_sg_rem_space = (u32)(iterator->sg_iter.length -
+			iterator->sg_iter.consumed);
+
+		count = min(size, current_sg_rem_space);
+		total_count += count;
+
+		location = shift_ptr(iterator->sg_iter.addr,
+				     iterator->sg_iter.consumed);
+
+		memcpy(location, buffer, count);
+
+		buffer = shift_ptr(buffer, count);
+		size -= count;
+		iterator->sg_iter.consumed += count;
+	}
+
+	return total_count;
+}
+
+int mausb_data_iterator_read(struct mausb_data_iter *iterator,
+			     u32 byte_num,
+			     struct list_head *data_chunks_list,
+			     u32 *data_chunks_num)
+{
+	if (iterator->buffer)
+		return mausb_read_virtual_buffer(iterator, byte_num,
+						 data_chunks_list,
+						 data_chunks_num);
+	else
+		return mausb_read_scatterlist_buffer(iterator, byte_num,
+						     data_chunks_list,
+						     data_chunks_num);
+}
+
+u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer,
+			      u32 length)
+{
+	if (iterator->buffer)
+		return mausb_write_virtual_buffer(iterator, buffer, length);
+	else
+		return mausb_write_scatterlist_buffer(iterator, buffer, length);
+}
+
+static inline void mausb_seek_virtual_buffer(struct mausb_data_iter *iterator,
+					     u32 seek_delta)
+{
+	iterator->offset += min(seek_delta, iterator->length -
+					    iterator->offset);
+}
+
+static void mausb_seek_scatterlist_buffer(struct mausb_data_iter *iterator,
+					  u32 seek_delta)
+{
+	u32 rem_bytes_in_current_scatter;
+
+	while (seek_delta) {
+		rem_bytes_in_current_scatter = (u32)(iterator->sg_iter.length -
+						iterator->sg_iter.consumed);
+		if (rem_bytes_in_current_scatter <= seek_delta) {
+			iterator->sg_iter.consumed +=
+			    rem_bytes_in_current_scatter;
+			seek_delta -= rem_bytes_in_current_scatter;
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		} else {
+			iterator->sg_iter.consumed += seek_delta;
+			break;
+		}
+	}
+}
+
+void mausb_data_iterator_seek(struct mausb_data_iter *iterator,
+			      u32 seek_delta)
+{
+	if (iterator->buffer)
+		mausb_seek_virtual_buffer(iterator, seek_delta);
+	else
+		mausb_seek_scatterlist_buffer(iterator, seek_delta);
+}
+
+static void mausb_calculate_buffer_length(struct mausb_data_iter *iterator)
+{
+	/* Calculate buffer length */
+	if (iterator->buffer_len > 0) {
+		/* Transfer_buffer_length is populated */
+		iterator->length = iterator->buffer_len;
+	} else if (iterator->sg && iterator->num_sgs != 0) {
+		/* Transfer_buffer_length is not populated */
+		sg_miter_start(&iterator->sg_iter, iterator->sg,
+			       iterator->num_sgs, iterator->flags);
+		while (sg_miter_next(&iterator->sg_iter))
+			iterator->length += (u32)iterator->sg_iter.length;
+		sg_miter_stop(&iterator->sg_iter);
+	} else {
+		iterator->length = 0;
+	}
+}
+
+void mausb_init_data_iterator(struct mausb_data_iter *iterator, void *buffer,
+			      u32 buffer_len, struct scatterlist *sg,
+			      unsigned int num_sgs, bool direction)
+{
+	iterator->offset = 0;
+	iterator->buffer     = buffer;
+	iterator->buffer_len = buffer_len;
+	iterator->length     = 0;
+	iterator->sg	     = sg;
+	iterator->num_sgs    = num_sgs;
+	iterator->sg_started = 0;
+
+	mausb_calculate_buffer_length(iterator);
+
+	if (!buffer && sg && num_sgs != 0) {
+		/* Scatterlist provided */
+		iterator->flags = direction ? SG_MITER_TO_SG : SG_MITER_FROM_SG;
+		sg_miter_start(&iterator->sg_iter, sg, num_sgs,
+			       iterator->flags);
+		iterator->sg_started = 1;
+	}
+}
+
+void mausb_uninit_data_iterator(struct mausb_data_iter *iterator)
+{
+	iterator->offset     = 0;
+	iterator->length     = 0;
+	iterator->buffer     = NULL;
+	iterator->buffer_len = 0;
+
+	if (iterator->sg_started)
+		sg_miter_stop(&iterator->sg_iter);
+
+	iterator->sg_started = 0;
+}
+
+void mausb_reset_data_iterator(struct mausb_data_iter *iterator)
+{
+	iterator->offset = 0;
+	if (iterator->sg_started) {
+		sg_miter_stop(&iterator->sg_iter);
+		iterator->sg_started = 0;
+	}
+
+	if (!iterator->buffer && iterator->sg && iterator->num_sgs != 0) {
+		sg_miter_start(&iterator->sg_iter, iterator->sg,
+			       iterator->num_sgs, iterator->flags);
+		iterator->sg_started = 1;
+	}
+}
+
+u32 mausb_data_iterator_length(struct mausb_data_iter *iterator)
+{
+	return iterator->length;
+}
diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h
new file mode 100644
index 000000000000..f184bbc07783
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal.h
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_H__
+#define __MAUSB_HPAL_H__
+
+#include <linux/kref.h>
+#include <linux/suspend.h>
+#include <linux/usb.h>
+
+#include "ip_link.h"
+#include "mausb_address.h"
+#include "mausb_event.h"
+
+#define MAUSB_CONTROL_SETUP_SIZE	8
+#define MAUSB_BUSY_RETRIES_COUNT	3
+#define MAUSB_HEARTBEAT_TIMEOUT_MS	1000
+#define MAUSB_CLIENT_STOPPED_TIMEOUT_MS	3000
+
+#define MAUSB_MAX_RECEIVE_FAILURES	3
+#define MAUSB_MAX_MISSED_HEARTBEATS	3
+#define MAUSB_TRANSFER_RESERVED		0
+
+#define MAUSB_CHANNEL_MAP_LENGTH	4
+
+extern struct mss mss;
+extern struct mausb_hcd *mhcd;
+
+enum mausb_isoch_header_format_size {
+	MAUSB_ISOCH_SHORT_FORMAT_SIZE	 = 4,
+	MAUSB_ISOCH_STANDARD_FORMAT_SIZE = 8,
+	MAUSB_ISOCH_LONG_FORMAT_SIZE	 = 12
+};
+
+struct mausb_completion {
+	struct list_head   list_entry;
+	struct completion  *completion_event;
+	struct mausb_event *mausb_event;
+	u64		   event_id;
+};
+
+struct mausb_mss_rings_events {
+	atomic_t	  mausb_stop_reading_ring_events;
+	struct completion mausb_ring_has_events;
+};
+
+struct mss {
+	bool	   deinit_in_progress;
+	spinlock_t lock;	/* Protect mss structure */
+	u64	   ring_buffer_id;
+
+	struct completion empty;
+	struct completion client_stopped;
+	bool		  client_connected;
+	struct timer_list heartbeat_timer;
+	u8		  missed_heartbeats;
+
+	struct list_head  madev_list;
+	atomic_t	  num_of_transitions_to_sleep;
+	struct list_head  available_ring_buffers;
+
+	struct mausb_mss_rings_events	 rings_events;
+	struct mausb_events_notification events[MAUSB_MAX_NUM_OF_MA_DEVS];
+};
+
+struct mausb_device {
+	struct mausb_device_address dev_addr;
+	struct net		    *net_ns;
+	struct list_head	    list_entry;
+
+	struct mausb_ip_ctx *mgmt_channel;
+	struct mausb_ip_ctx *ctrl_channel;
+	struct mausb_ip_ctx *bulk_channel;
+	struct mausb_ip_ctx *isoch_channel;
+	struct mausb_ip_ctx *channel_map[MAUSB_CHANNEL_MAP_LENGTH];
+
+	struct work_struct work;
+	struct work_struct socket_disconnect_work;
+	struct work_struct hcd_disconnect_work;
+	struct work_struct madev_delete_work;
+	struct work_struct ping_work;
+	struct work_struct heartbeat_work;
+	struct workqueue_struct *workq;
+
+	struct kref refcount;
+	/* Set on port change event after cap resp */
+	u8 dev_type;
+	u8 dev_speed;
+	u8 lse;
+	u8 madev_addr;
+	u8 dev_connected;
+	u16 id;
+	u16 port_number;
+
+	u64		event_id;
+	spinlock_t	event_id_lock; /* Lock event ID increments */
+
+	struct list_head completion_events;
+	spinlock_t	 completion_events_lock; /* Lock completion events */
+
+	struct completion user_finished_event;
+	u16		  num_of_user_events;
+	u16		  num_of_completed_events;
+
+	spinlock_t	  num_of_user_events_lock; /* Lock user events count */
+
+	struct timer_list connection_timer;
+	u8		  receive_failures_num;
+	spinlock_t	  connection_timer_lock; /* Lock connection timer */
+
+	atomic_t	  unresponsive_client;
+
+	atomic_t	  num_of_usb_devices;
+};
+
+struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb);
+struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
+							    int status);
+struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr);
+
+static inline u64 mausb_event_id(struct mausb_device *dev)
+{
+	unsigned long flags;
+	u64 val;
+
+	spin_lock_irqsave(&dev->event_id_lock, flags);
+	val = ++(dev->event_id);
+	spin_unlock_irqrestore(&dev->event_id_lock, flags);
+
+	return val;
+}
+
+int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
+				 struct urb *request);
+int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
+		       u64 event_id);
+int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep);
+
+static inline void mausb_insert_event(struct mausb_device *dev,
+				      struct mausb_completion *event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_add_tail(&event->list_entry, &dev->completion_events);
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+}
+
+static inline void mausb_remove_event(struct mausb_device *dev,
+				      struct mausb_completion *event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_del(&event->list_entry);
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+}
+
+void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
+void mausb_complete_urb(struct mausb_event *event);
+void mausb_reset_connection_timer(struct mausb_device *dev);
+void mausb_reset_heartbeat_cnt(void);
+void mausb_release_event_resources(struct mausb_event  *event);
+void mausb_initialize_mss(void);
+void mausb_deinitialize_mss(void);
+int mausb_register_power_state_listener(void);
+void mausb_unregister_power_state_listener(void);
+
+void mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *
+				       std_desc,
+				       struct usb_endpoint_descriptor *
+				       usb_std_desc);
+void mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *
+					 ss_desc,
+					 struct usb_endpoint_descriptor *
+					 usb_std_desc,
+					 struct usb_ss_ep_comp_descriptor *
+					 usb_ss_desc);
+
+int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num,
+		    struct mausb_kvec_data_wrapper *data);
+
+int mausb_send_transfer_ack(struct mausb_device *dev,
+			    struct mausb_event *event);
+
+int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event);
+
+int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event);
+
+int mausb_add_data_chunk(void *buffer, u32 buffer_size,
+			 struct list_head *chunks_list);
+
+int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data,
+			    struct list_head *chunks_list,
+			    u32 num_of_data_chunks);
+
+void mausb_cleanup_chunks_list(struct list_head *chunks_list);
+
+static inline bool mausb_ctrl_transfer(struct ma_usb_hdr_common *hdr)
+{
+	return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL;
+}
+
+static inline bool mausb_isoch_transfer(struct ma_usb_hdr_common *hdr)
+{
+	return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH;
+}
+
+static inline bool mausb_ctrl_data_event(struct mausb_event *event)
+{
+	return event->data.transfer_type ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL;
+}
+
+static inline bool mausb_isoch_data_event(struct mausb_event *event)
+{
+	return event->data.transfer_type ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH;
+}
+
+/* usb to mausb transfer type */
+static inline
+u8 mausb_transfer_type_from_usb(struct usb_endpoint_descriptor *epd)
+{
+	return (u8)usb_endpoint_type(epd) << 3;
+}
+
+static inline u8 mausb_transfer_type_from_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK;
+}
+
+static inline
+enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type)
+{
+	return transfer_type >> 3;
+}
+
+struct mausb_data_iter {
+	u32 length;
+
+	void *buffer;
+	u32  buffer_len;
+	u32  offset;
+
+	struct scatterlist	*sg;
+	struct sg_mapping_iter	sg_iter;
+	bool		sg_started;
+	unsigned int	num_sgs;
+	unsigned int	flags;
+};
+
+struct mausb_payload_chunk {
+	struct list_head list_entry;
+	struct kvec	 kvec;
+};
+
+int mausb_data_iterator_read(struct mausb_data_iter *iterator,
+			     u32 byte_num,
+			     struct list_head *data_chunks_list,
+			     u32 *data_chunks_num);
+
+u32 mausb_data_iterator_length(struct mausb_data_iter *iterator);
+u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer,
+			      u32 length);
+
+void mausb_init_data_iterator(struct mausb_data_iter *iterator,
+			      void *buffer, u32 buffer_len,
+			      struct scatterlist *sg, unsigned int num_sgs,
+			      bool direction);
+void mausb_reset_data_iterator(struct mausb_data_iter *iterator);
+void mausb_uninit_data_iterator(struct mausb_data_iter *iterator);
+void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta);
+
+static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
+						unsigned int elem_size)
+{
+	unsigned int num_of_pages = DIV_ROUND_UP(num_of_elems * elem_size,
+						 PAGE_SIZE);
+	unsigned int order = (unsigned int)ilog2(num_of_pages) +
+					(is_power_of_2(num_of_pages) ? 0 : 1);
+	return order;
+}
+
+#endif /* __MAUSB_HPAL_H__ */
diff --git a/drivers/usb/mausb_host/ma_usb.h b/drivers/usb/mausb_host/ma_usb.h
new file mode 100644
index 000000000000..65f6229c0dfe
--- /dev/null
+++ b/drivers/usb/mausb_host/ma_usb.h
@@ -0,0 +1,869 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MA_USB_H__
+#define __MAUSB_MA_USB_H__
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <types.h>
+#endif /* __KERNEL__ */
+
+#define MA_USB_SET_FIELD_(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, _v)
+#define MA_USB_GET_FIELD_(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, _v)
+#define MA_USB_SET_FIELD(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, \
+						    MA_USB_##_v)
+#define MA_USB_GET_FIELD(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, \
+						    MA_USB_##_v)
+
+#define MA_USB_MGMT_TOKEN_RESERVED  0
+#define MA_USB_MGMT_TOKEN_MIN       1
+#define MA_USB_MGMT_TOKEN_MAX       ((1 << 10) - 1)
+
+#define MA_USB_DATA_EPS_UNASSIGNED  0
+#define MA_USB_DATA_EPS_ACTIVE      1
+#define MA_USB_DATA_EPS_INACTIVE    2
+#define MA_USB_DATA_EPS_HALTED      3
+
+#define MA_USB_DATA_TFLAGS_ARQ      1
+#define MA_USB_DATA_TFLAGS_NEG      2
+#define MA_USB_DATA_TFLAGS_EOT      4
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL   0
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH  8
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_BULK   16
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_INTR   24
+
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK   0x18
+
+#define MA_USB_DATA_IFLAGS_MTD_VALID      1
+#define MA_USB_DATA_IFLAGS_HDR_FMT_SHORT  0
+#define MA_USB_DATA_IFLAGS_HDR_FMT_STD    2
+#define MA_USB_DATA_IFLAGS_HDR_FMT_LONG   4
+#define MA_USB_DATA_IFLAGS_IRS_FMT_STD    0
+#define MA_USB_DATA_IFLAGS_IRS_FMT_LONG   2
+#define MA_USB_DATA_IFLAGS_ASAP           8
+
+#define MA_USB_DATA_IFLAGS_FMT_MASK       0x6
+
+/* version */
+
+#define MA_USB_HDR_VERSION_1_0      0
+
+/* flags */
+
+#define MA_USB_HDR_FLAGS_HOST       1
+#define MA_USB_HDR_FLAGS_RETRY      2
+#define MA_USB_HDR_FLAGS_TIMESTAMP  4
+#define MA_USB_HDR_FLAGS_RESERVED   8
+#define MA_USB_HDR_FLAG(_f) MA_USB_HDR_FLAGS_##_f
+
+/* type and subtype */
+
+#define MA_USB_HDR_TYPE_TYPE_MASK     0xC0
+#define MA_USB_HDR_TYPE_SUBTYPE_MASK  0x3F
+
+#define MA_USB_HDR_TYPE_TYPE_MANAGEMENT 0
+#define MA_USB_HDR_TYPE_TYPE_CONTROL    1
+#define MA_USB_HDR_TYPE_TYPE_DATA       2
+
+/* Management subtypes */
+
+#define _MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2)
+#define _MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 1)
+
+#define MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \
+	_MA_USB_HDR_TYPE_MANAGEMENT_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \
+	_MA_USB_HDR_TYPE_MANAGEMENT_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+
+#define MA_USB_HDR_TYPE_SUBTYPE_CAP               0
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVHANDLE      1
+#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLE          2
+#define MA_USB_HDR_TYPE_SUBTYPE_EPACTIVATE        3
+#define MA_USB_HDR_TYPE_SUBTYPE_EPINACTIVATE      4
+#define MA_USB_HDR_TYPE_SUBTYPE_EPRESET           5
+#define MA_USB_HDR_TYPE_SUBTYPE_CLEARTRANSFERS    6
+#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLEDELETE    7
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVRESET          8
+#define MA_USB_HDR_TYPE_SUBTYPE_MODIFYEP0         9
+#define MA_USB_HDR_TYPE_SUBTYPE_SETUSBDEVADDR     10
+#define MA_USB_HDR_TYPE_SUBTYPE_UPDATEDEV         11
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVDISCONNECT  12
+#define MA_USB_HDR_TYPE_SUBTYPE_USBSUSPEND        13
+#define MA_USB_HDR_TYPE_SUBTYPE_USBRESUME         14
+#define MA_USB_HDR_TYPE_SUBTYPE_REMOTEWAKE        15
+#define MA_USB_HDR_TYPE_SUBTYPE_PING              16
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVDISCONNECT     17
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVINITDISCONNECT 18
+#define MA_USB_HDR_TYPE_SUBTYPE_SYNCH             19
+#define MA_USB_HDR_TYPE_SUBTYPE_CANCELTRANSFER    20
+#define MA_USB_HDR_TYPE_SUBTYPE_EPOPENSTREAM      21
+#define MA_USB_HDR_TYPE_SUBTYPE_EPCLOSESTREAM     22
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVRESET       23
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVNOTIFICATION   24
+#define MA_USB_HDR_TYPE_SUBTYPE_EPSETKEEPALIVE    25
+#define MA_USB_HDR_TYPE_SUBTYPE_GETPORTBW         26
+#define MA_USB_HDR_TYPE_SUBTYPE_SLEEP             27
+#define MA_USB_HDR_TYPE_SUBTYPE_WAKE              28
+#define MA_USB_HDR_TYPE_SUBTYPE_VENDORSPECIFIC    31 /* Reserved */
+
+/* Data subtypes */
+
+#define _MA_USB_HDR_TYPE_DATA_REQ(_s) ({ \
+	typeof(_s) (s) = (_s); \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 \
+	+ ((s) > 0 ? 1 : 0)); })
+#define _MA_USB_HDR_TYPE_DATA_RESP(_s) ({ \
+	typeof(_s) (s) = (_s); \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 + 1 \
+	+ ((s) > 0 ? 1 : 0)); })
+#define _MA_USB_HDR_TYPE_DATA_ACK(_s) ( \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 2))
+
+#define MA_USB_HDR_TYPE_DATA_REQ(_s) \
+	_MA_USB_HDR_TYPE_DATA_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_DATA_RESP(_s) \
+	_MA_USB_HDR_TYPE_DATA_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_DATA_ACK(_s) \
+	_MA_USB_HDR_TYPE_DATA_ACK(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+
+#define MA_USB_HDR_TYPE_SUBTYPE_TRANSFER          0
+#define MA_USB_HDR_TYPE_SUBTYPE_ISOCHTRANSFER     1
+
+/* EP/Device Handle */
+
+#define MA_USB_DEVICE_HANDLE_RESERVED   0
+
+#define MA_USB_EP_HANDLE_D_MASK     0x0001
+#define MA_USB_EP_HANDLE_EP_N_MASK  0x001e
+#define MA_USB_EP_HANDLE_ADDR_MASK  0x0fe0
+#define MA_USB_EP_HANDLE_BUS_N_MASK 0xf000
+
+#define MA_USB_EP_HANDLE(_b, _a, _e, _d) ( \
+	MA_USB_SET_FIELD_(EP_HANDLE_BUS_N, _b)  | \
+	MA_USB_SET_FIELD_(EP_HANDLE_ADDR, _a)   | \
+	MA_USB_SET_FIELD_(EP_HANDLE_EP_N, _e)   | \
+	MA_USB_SET_FIELD_(EP_HANDLE_D, _d))
+
+#define MA_USB_EP_HANDLE_BUS_N_VIRTUAL  15
+#define MA_USB_EP_HANDLE_ADDR_DEFAULT   0
+#define MA_USB_EP_HANDLE_EP_N_DEFAULT   0
+#define MA_USB_EP_HANDLE_D_OUT          0	/* See USB2.0 9.3, Table 9-2 */
+#define MA_USB_EP_HANDLE_D_IN           1	/* See USB2.0 9.3, Table 9-2 */
+
+/* Status codes */
+
+#define MA_USB_HDR_STATUS_UNSUCCESSFUL                  -128
+#define MA_USB_HDR_STATUS_INVALID_MA_USB_SESSION_STATE  -127
+#define MA_USB_HDR_STATUS_INVALID_DEVICE_HANDLE         -126
+#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE             -125
+#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE_STATE       -124
+#define MA_USB_HDR_STATUS_INVALID_REQUEST               -123
+#define MA_USB_HDR_STATUS_MISSING_SEQUENCE_NUMBER       -122
+#define MA_USB_HDR_STATUS_TRANSFER_PENDING              -121
+#define MA_USB_HDR_STATUS_TRANSFER_EP_STALL             -120
+#define MA_USB_HDR_STATUS_TRANSFER_SIZE_ERROR           -119
+#define MA_USB_HDR_STATUS_TRANSFER_DATA_BUFFER_ERROR    -118
+#define MA_USB_HDR_STATUS_TRANSFER_BABBLE_DETECTED      -117
+#define MA_USB_HDR_STATUS_TRANSFER_TRANSACTION_ERROR    -116
+#define MA_USB_HDR_STATUS_TRANSFER_SHORT_TRANSFER       -115
+#define MA_USB_HDR_STATUS_TRANSFER_CANCELED             -114
+#define MA_USB_HDR_STATUS_INSUFICIENT_RESOURCES         -113
+#define MA_USB_HDR_STATUS_NOT_SUFFICIENT_BANDWIDTH      -112
+#define MA_USB_HDR_STATUS_INTERNAL_ERROR                -111
+#define MA_USB_HDR_STATUS_DATA_OVERRUN                  -110
+#define MA_USB_HDR_STATUS_DEVICE_NOT_ACCESSED           -109
+#define MA_USB_HDR_STATUS_BUFFER_OVERRUN                -108
+#define MA_USB_HDR_STATUS_BUSY                          -107
+#define MA_USB_HDR_STATUS_DROPPED_PACKET                -106
+#define MA_USB_HDR_STATUS_ISOCH_TIME_EXPIRED            -105
+#define MA_USB_HDR_STATUS_ISOCH_TIME_INVALID            -104
+#define MA_USB_HDR_STATUS_NO_USB_PING_RESPONSE          -103
+#define MA_USB_HDR_STATUS_NOT_SUPPORTED                 -102
+#define MA_USB_HDR_STATUS_REQUEST_DENIED                -101
+#define MA_USB_HDR_STATUS_MISSING_REQUEST_ID            -100
+#define MA_USB_HDR_STATUS_SUCCESS                       0	/* Reserved */
+#define MA_USB_HDR_STATUS_NO_ERROR MA_USB_HDR_STATUS_SUCCESS	/* Reserved */
+
+/* Speed values */
+
+#define MA_USB_SPEED_LOW_SPEED         0
+#define MA_USB_SPEED_FULL_SPEED        1
+#define MA_USB_SPEED_HIGH_SPEED        2
+#define MA_USB_SPEED_SUPER_SPEED       3
+#define MA_USB_SPEED_SUPER_SPEED_PLUS  4
+
+/* capreq extra hdr */
+
+#define MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH\
+	(sizeof(struct ma_usb_desc) +\
+	sizeof(struct ma_usb_capreq_desc_synchronization))
+#define MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH\
+	(sizeof(struct ma_usb_desc) +\
+	sizeof(struct ma_usb_capreq_desc_link_sleep))
+
+#define MA_USB_CAPREQ_LENGTH\
+	(sizeof(struct ma_usb_hdr_common) +\
+	sizeof(struct ma_usb_hdr_capreq) +\
+	MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH +\
+	MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH)
+
+/* capreq desc types */
+
+#define MA_USB_CAPREQ_DESC_TYPE_SYNCHRONIZATION 3
+#define MA_USB_CAPREQ_DESC_TYPE_LINK_SLEEP      5
+
+/* capresp descriptors */
+
+#define MA_USB_CAPRESP_DESC_TYPE_SPEED            0
+#define MA_USB_CAPRESP_DESC_TYPE_P_MANAGED_OUT    1
+#define MA_USB_CAPRESP_DESC_TYPE_ISOCHRONOUS      2
+#define MA_USB_CAPRESP_DESC_TYPE_SYNCHRONIZATION  3
+#define MA_USB_CAPRESP_DESC_TYPE_CONTAINER_ID     4
+#define MA_USB_CAPRESP_DESC_TYPE_LINK_SLEEP       5
+#define MA_USB_CAPRESP_DESC_TYPE_HUB_LATENCY      6
+
+/* Request ID and sequence number values */
+
+#define MA_USB_TRANSFER_RESERVED      0
+#define MA_USB_TRANSFER_REQID_MIN     0
+#define MA_USB_TRANSFER_REQID_MAX     ((1 <<  8) - 1)
+#define MA_USB_TRANSFER_SEQN_MIN      0
+#define MA_USB_TRANSFER_SEQN_MAX      ((1 << 24) - 2)
+#define MA_USB_TRANSFER_SEQN_INVALID  ((1 << 24) - 1)
+
+#define MA_USB_ISOCH_SFLAGS_FRAGMENT      0x1
+#define MA_USB_ISOCH_SFLAGS_LAST_FRAGMENT 0x2
+
+#define MAUSB_MAX_MGMT_SIZE 50
+
+#define MAUSB_TRANSFER_HDR_SIZE (u32)(sizeof(struct ma_usb_hdr_common) +\
+				      sizeof(struct ma_usb_hdr_transfer))
+
+#define MAUSB_ISOCH_TRANSFER_HDR_SIZE (u16)(sizeof(struct ma_usb_hdr_common) +\
+			sizeof(struct ma_usb_hdr_isochtransfer) +\
+			sizeof(struct ma_usb_hdr_isochtransfer_optional))
+
+#define MAX_ISOCH_ASAP_PACKET_SIZE (150000 /* Network MTU */ -\
+	MAUSB_ISOCH_TRANSFER_HDR_SIZE - 20 /* IP header size */ -\
+	8 /* UDP header size*/)
+
+#define shift_ptr(ptr, offset) ((u8 *)(ptr) + (offset))
+
+/* USB descriptor */
+struct ma_usb_desc {
+	u8 length;
+	u8 type;
+	u8 value[0];
+} __packed;
+
+struct ma_usb_ep_handle {
+	u16 d		:1,
+	    ep_n	:4,
+	    addr	:7,
+	    bus_n	:4;
+};
+
+struct ma_usb_hdr_mgmt {
+	u32 status	:8,
+	    token	:10,  /* requestor originator allocated */
+	    reserved	:14;
+} __aligned(4);
+
+struct ma_usb_hdr_ctrl {	/* used in all req/resp/conf operations */
+	s8 status;
+	u8 link_type;
+	union {
+		u8 tid;	/* ieee 802.11 */
+	} connection_id;
+} __aligned(4);
+
+struct ma_usb_hdr_data {
+	s8 status;
+	u8 eps		:2,
+	   t_flags	:6;
+	union {
+		u16 stream_id;
+		struct {
+			u16 headers	:12,
+			    i_flags	:4;
+		};
+	};
+} __aligned(4);
+
+struct ma_usb_hdr_common {
+	u8 version	:4,
+	   flags	:4;
+	u8  type;
+	u16 length;
+	union {
+		u16 dev;
+		u16 epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u8 dev_addr;
+	u8 ssid;
+	union {
+		s8 status;
+		struct ma_usb_hdr_mgmt mgmt;
+		struct ma_usb_hdr_ctrl ctrl;
+		struct ma_usb_hdr_data data;
+	};
+} __aligned(4);
+
+/* capreq extra hdr */
+
+struct ma_usb_hdr_capreq {
+	u32 out_mgmt_reqs	:12,
+	    reserved		:20;
+} __aligned(4);
+
+struct ma_usb_capreq_desc_synchronization {
+	u8 media_time_available	:1,
+	   reserved		:7;
+} __packed;
+
+struct ma_usb_capreq_desc_link_sleep {
+	u8 link_sleep_capable	:1,
+	   reserved		:7;
+} __packed;
+
+/* capresp extra hdr */
+
+struct ma_usb_hdr_capresp {
+	u16 endpoints;
+	u8 devices;
+	u8 streams		:5,
+	   dev_type		:3;
+	u32 descs		:8,
+	    descs_length	:24;
+	u16 out_transfer_reqs;
+	u16 out_mgmt_reqs	:12,
+	    reserved		:4;
+} __aligned(4);
+
+struct ma_usb_capresp_desc_speed {
+	u8 reserved1		:4,
+		speed		:4;
+	u8 reserved2		:4,
+	   lse			:2,	/* USB3.1 8.5.6.7, Table 8-22 */
+	   reserved3		:2;
+} __packed;
+
+struct ma_usb_capresp_desc_p_managed_out {
+	u8 elastic_buffer		:1,
+	   drop_notification		:1,
+	   reserved			:6;
+} __packed;
+
+struct ma_usb_capresp_desc_isochronous {
+	u8 payload_dword_aligned	:1,
+	   reserved			:7;
+} __packed;
+
+struct ma_usb_capresp_desc_synchronization {
+	u8 media_time_available	:1,
+	   time_stamp_required	:1,/* hubs need this set */
+	   reserved		:6;
+} __packed;
+
+struct ma_usb_capresp_desc_container_id {
+	u8 container_id[16];	/* UUID IETF RFC 4122 */
+} __packed;
+
+struct ma_usb_capresp_desc_link_sleep {
+	u8 link_sleep_capable	:1,
+	   reserved		:7;
+} __packed;
+
+struct ma_usb_capresp_desc_hub_latency {
+	u16 latency;		/* USB3.1 */
+} __packed;
+
+/* usbdevhandlereq extra hdr */
+struct ma_usb_hdr_usbdevhandlereq {
+	u32 route_string	:20,
+	    speed		:4,
+	    reserved1		:8;
+	u16 hub_dev_handle;
+	u16 reserved2;
+	u16 parent_hs_hub_dev_handle;
+	u16 parent_hs_hub_port		:4,
+	    mtt				:1,	/* USB2.0 11.14, 11.14.1.3 */
+	    lse				:2,	/* USB3.1 8.5.6.7, Table 8-22 */
+	    reserved3			:9;
+} __aligned(4);
+
+/* usbdevhandleresp extra hdr */
+struct ma_usb_hdr_usbdevhandleresp {
+	u16 dev_handle;
+	u16 reserved;
+} __aligned(4);
+
+/* ephandlereq extra hdr */
+struct ma_usb_hdr_ephandlereq {
+	u32 ep_descs		:5,
+	    ep_desc_size	:6,
+	    reserved		:21;
+} __aligned(4);
+
+/*
+ * Restricted USB2.0 ep desc limited to 6 bytes, isolating further changes.
+ * See USB2.0 9.6.6, Table 9-13
+ */
+struct usb_ep_desc {
+	u8 bLength;
+	/* USB2.0 9.4, Table 9-5 (5) usb/ch9.h: USB_DT_ENDPOINT */
+	u8 bDescriptorType;
+	u8  bEndpointAddress;
+	u8  bmAttributes;
+	__le16 wMaxPacketSize;
+	u8  bInterval;
+} __packed;
+
+/*
+ * Restricted USB3.1 ep comp desc isolating further changes in usb/ch9.h
+ * See USB3.1 9.6.7, Table 9-26
+ */
+struct usb_ss_ep_comp_desc {
+	u8 bLength;
+	/* USB3.1 9.4, Table 9-6 (48) usb/ch9.h: USB_DT_SS_ENDPOINT_COMP */
+	u8  bDescriptorType;
+	u8  bMaxBurst;
+	u8  bmAttributes;
+	__le16 wBytesPerInterval;
+} __packed;
+
+/*
+ * USB3.1 ss_plus_isoch_ep_comp_desc
+ * See USB3.1 9.6.8, Table 9-27
+ */
+struct usb_ss_plus_isoch_ep_comp_desc {
+	u8 bLength;
+	/* USB3.1 9.4, Table 9-6 (49) usb/ch9.h: not yet defined! */
+	u8 bDescriptorType;
+	u16 wReserved;
+	u32 dwBytesPerInterval;
+} __packed;
+
+struct ma_usb_ephandlereq_desc_std {
+	struct usb_ep_desc usb20;
+} __aligned(4);
+
+struct ma_usb_ephandlereq_desc_ss {
+	struct usb_ep_desc	   usb20;
+	struct usb_ss_ep_comp_desc usb31;
+} __aligned(4);
+
+struct ma_usb_ephandlereq_desc_ss_plus {
+	struct usb_ep_desc		      usb20;
+	struct usb_ss_ep_comp_desc	      usb31;
+	struct usb_ss_plus_isoch_ep_comp_desc usb31_isoch;
+} __aligned(4);
+
+struct ma_usb_dev_context {
+	struct usb_ep_desc usb;
+};
+
+/* ephandleresp extra hdr */
+struct ma_usb_hdr_ephandleresp {
+	u32 ep_descs :5,
+	    reserved :27;
+} __aligned(4);
+
+/* ephandleresp descriptor */
+struct ma_usb_ephandleresp_desc {
+	union {
+		struct ma_usb_ep_handle eph;
+		u16		epv;
+	} ep_handle;
+	u16 d		:1,	/* non-control or non-OUT */
+	    isoch	:1,
+	    l_managed	:1,	/* control or non-isoch OUT */
+	    invalid	:1,
+	    reserved1	:12;
+	u16 ccu;		/* control or non-isoch OUT */
+	u16 reserved2;
+	u32 buffer_size;	/* control or OUT */
+	u16 isoch_prog_delay;	/* in us. */
+	u16 isoch_resp_delay;	/* in us. */
+} __aligned(4);
+
+/* epactivatereq extra hdr */
+struct ma_usb_hdr_epactivatereq {
+	u32 ep_handles	:5,
+	    reserved	:27;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epactivateresp extra hdr */
+struct ma_usb_hdr_epactivateresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epinactivatereq extra hdr */
+struct ma_usb_hdr_epinactivatereq {
+	u32 ep_handles	:5,
+	    suspend	:1,
+	    reserved	:26;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epinactivateresp extra hdr */
+struct ma_usb_hdr_epinactivateresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epresetreq extra hdr */
+struct ma_usb_hdr_epresetreq {
+	u32 ep_reset_blocks	:5,
+	    reserved		:27;
+} __aligned(4);
+
+/* epresetreq reset block */
+struct ma_usb_epresetreq_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 tsp		:1,
+	    reserved	:15;
+} __aligned(4);
+
+/* epresetresp extra hdr */
+struct ma_usb_hdr_epresetresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* cleartransfersreq extra hdr */
+struct ma_usb_hdr_cleartransfersreq {
+	u32 info_blocks	:8,
+	    reserved	:24;
+} __aligned(4);
+
+/* cleartransfersreq info block */
+struct ma_usb_cleartransfersreq_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id; /* ss stream eps only */
+	u32 start_req_id	:8,
+	    reserved		:24;
+} __aligned(4);
+
+/* cleartransfersresp extra hdr */
+struct ma_usb_hdr_cleartransfersresp {
+	u32 status_blocks	:8,
+	    reserved		:24;
+} __aligned(4);
+
+/* cleartransfersresp status block */
+struct ma_usb_cleartransfersresp_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;	/* ss stream eps only */
+	u32 cancel_success	:1,
+	    partial_delivery	:1,
+	    reserved		:30;
+	u32 last_req_id		:8,
+	    delivered_seq_n	:24;	/* OUT w/partial_delivery only */
+	u32 delivered_byte_offset;	/* OUT w/partial_delivery only */
+} __aligned(4);
+
+/* ephandledeletereq extra hdr */
+struct ma_usb_hdr_ephandledeletereq {
+	u32 ep_handles	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* ephandledeleteresp extra hdr */
+struct ma_usb_hdr_ephandledeleteresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* modifyep0req extra hdr */
+struct ma_usb_hdr_modifyep0req {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 max_packet_size;
+} __aligned(4);
+
+/*
+ * modifyep0resp extra hdr
+ * Only if req ep0 handle addr was 0 and req dev is in the addressed state
+ * or  if req ep0 handle addr != 0 and req dev is in default state
+ */
+struct ma_usb_hdr_modifyep0resp {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+
+	u16 reserved;
+} __aligned(4);
+
+/* setusbdevaddrreq extra hdr */
+struct ma_usb_hdr_setusbdevaddrreq {
+	u16 response_timeout;	/* in ms. */
+	u16 reserved;
+} __aligned(4);
+
+/* setusbdevaddrresp extra hdr */
+struct ma_usb_hdr_setusbdevaddrresp {
+	u32 addr	:7,
+	    reserved	:25;
+} __aligned(4);
+
+/* updatedevreq extra hdr */
+struct ma_usb_hdr_updatedevreq {
+	u16 max_exit_latency;	/* hubs only */
+	u8 hub		:1,
+	   ports	:4,
+	   mtt		:1,
+	   ttt		:2;
+	u8 integrated_hub_latency	:1,
+	   reserved			:7;
+} __aligned(4);
+
+/*
+ * USB2.0 dev desc, isolating further changes in usb/ch9.h
+ * See USB2.0 9.6.6, Table 9-13
+ */
+struct usb_dev_desc {
+	u8 bLength;
+	/*
+	 * USB2.0 9.4, Table 9-5 (1)
+	 * usb/ch9.h: USB_DT_DEVICE
+	 */
+	u8 bDescriptorType;
+	__le16 bcdUSB;
+	u8  bDeviceClass;
+	u8  bDeviceSubClass;
+	u8  bDeviceProtocol;
+	u8  bMaxPacketSize0;
+	__le16 idVendor;
+	__le16 idProduct;
+	__le16 bcdDevice;
+	u8  iManufacturer;
+	u8  iProduct;
+	u8  iSerialNumber;
+	u8  bNumConfigurations;
+} __packed;
+
+struct ma_usb_updatedevreq_desc {
+	struct usb_dev_desc usb20;
+} __aligned(4);
+
+/* remotewakereq extra hdr */
+struct ma_usb_hdr_remotewakereq {
+	u32 resumed  :1,
+		 reserved :31;
+} __aligned(4);
+
+/* synchreq/resp extra hdr */
+struct ma_usb_hdr_synch {
+	u32 mtd_valid		:1,	/* MA-USB1.0b 6.5.1.8, Table 66 */
+	    resp_required	:1,
+	    reserved		:30;
+	union {
+		u32 timestamp;		/* MA-USB1.0b 6.5.1.11 */
+		struct {
+			u32 delta		:13,
+			    bus_interval	:19;
+		};			/* MA-USB1.0b 6.6.1, Table 69 */
+	};
+	u32 mtd;			/* MA-USB1.0b 6.5.1.12 */
+} __aligned(4);
+
+/* canceltransferreq extra hdr */
+struct ma_usb_hdr_canceltransferreq {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;
+	u32 req_id	  :8,
+		 reserved :24;
+} __aligned(4);
+
+/* canceltransferresp extra hdr */
+struct ma_usb_hdr_canceltransferresp {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;
+	u32 req_id		:8,
+	    cancel_status	:3,
+	    reserved1		:21;
+	u32 delivered_seq_n	:24,
+	    reserved2		:8;
+	u32 delivered_byte_offset;
+} __aligned(4);
+
+/* transferreq/resp/ack extra hdr */
+struct ma_usb_hdr_transfer {
+	u32 seq_n	:24,
+	    req_id	:8;
+	union {
+		u32 rem_size_credit;
+		/* ISOCH aliased fields added for convenience. */
+		struct {
+			u32 presentation_time	:20,
+			    segments		:12;
+		};
+	};
+} __aligned(4);
+
+/* isochtransferreq/resp extra hdr */
+struct ma_usb_hdr_isochtransfer {
+	u32 seq_n		:24,
+	    req_id		:8;
+	u32 presentation_time	:20,
+	    segments		:12;
+} __aligned(4);
+
+/* isochtransferreq/resp optional hdr */
+struct ma_usb_hdr_isochtransfer_optional {
+	union {
+		u32 timestamp;	/* MA-USB1.0b 6.5.1.11 */
+		struct {
+			u32 delta		:13,
+			    bus_interval	:19;
+		};		/* MA-USB1.0b 6.6.1, Table 69 */
+	};
+	u32 mtd;		/* MA-USB1.0b 6.5.1.12 */
+} __aligned(4);
+
+/* isochdatablock hdrs */
+
+struct ma_usb_hdr_isochdatablock_short {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+} __aligned(4);
+
+struct ma_usb_hdr_isochdatablock_std {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+	u16 segment_length;
+	u16 fragment_offset;
+} __aligned(4);
+
+struct ma_usb_hdr_isochdatablock_long {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+	u32 segment_length;
+	u32 fragment_offset;
+} __aligned(4);
+
+/* isochreadsizeblock hdrs */
+
+struct ma_usb_hdr_isochreadsizeblock_std {
+	u32 service_intervals		:12,
+	    max_segment_length		:20;
+} __aligned(4);
+
+struct ma_usb_hdr_isochreadsizeblock_long {
+	u32 service_intervals		:12,
+	    reserved			:20;
+	u32 max_segment_length;
+} __aligned(4);
+
+static inline int __mausb_set_field(int m, int v)
+{
+	return ((~((m) - 1) & (m)) * (v)) & (m);
+}
+
+static inline int __mausb_get_field(int m, int v)
+{
+	return ((v) & (m)) / (~((m) - 1) & (m));
+}
+
+static inline bool mausb_is_management_hdr_type(int hdr_type)
+{
+	return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type)
+			== MA_USB_HDR_TYPE_TYPE_MANAGEMENT;
+}
+
+static inline bool mausb_is_data_hdr_type(int hdr_type)
+{
+	return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type)
+			== MA_USB_HDR_TYPE_TYPE_DATA;
+}
+
+static inline bool mausb_is_management_resp_hdr_type(int hdr_resp_type)
+{
+	return mausb_is_management_hdr_type(hdr_resp_type) &&
+			(MA_USB_GET_FIELD_(HDR_TYPE_SUBTYPE, hdr_resp_type) & 1)
+			!= 0;
+}
+
+static inline
+struct ma_usb_hdr_transfer *
+mausb_get_data_transfer_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_transfer *)shift_ptr(hdr, sizeof(*hdr));
+}
+
+static inline
+struct ma_usb_hdr_isochtransfer *
+mausb_get_isochtransfer_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_isochtransfer *)shift_ptr(hdr, sizeof(*hdr));
+}
+
+static inline
+struct ma_usb_hdr_isochtransfer_optional *
+mausb_hdr_isochtransfer_optional_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_isochtransfer_optional *)
+			shift_ptr(hdr, sizeof(struct ma_usb_hdr_common) +
+				       sizeof(struct ma_usb_hdr_isochtransfer));
+}
+
+#endif	/* __MAUSB_MA_USB_H__ */
diff --git a/drivers/usb/mausb_host/mausb_address.h b/drivers/usb/mausb_host/mausb_address.h
new file mode 100644
index 000000000000..1a75482740ea
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_address.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_ADDRESS_H__
+#define __MAUSB_MAUSB_ADDRESS_H__
+
+#include <linux/inet.h>
+#include <linux/types.h>
+
+struct mausb_device_address {
+	u8 link_type;
+	struct {
+		char address[INET6_ADDRSTRLEN];
+		u8 number_of_ports;
+		struct {
+			u16 management;
+			u16 control;
+			u16 bulk;
+			u16 interrupt;
+			u16 isochronous;
+		} port;
+	} ip;
+};
+
+#endif /* __MAUSB_MAUSB_ADDRESS_H__ */
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
index 3ce90c29f6de..8730590126ea 100644
--- a/drivers/usb/mausb_host/mausb_core.c
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -12,6 +12,7 @@
 #include <linux/net.h>
 
 #include "hcd.h"
+#include "hpal.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
@@ -75,12 +76,20 @@ static int mausb_host_init(void)
 	if (status < 0)
 		goto cleanup;
 
-	status = mausb_create_dev();
+	status = mausb_register_power_state_listener();
 	if (status < 0)
 		goto cleanup_hcd;
 
+	status = mausb_create_dev();
+	if (status < 0)
+		goto unregister_power_state_listener;
+
+	mausb_initialize_mss();
+
 	return 0;
 
+unregister_power_state_listener:
+	mausb_unregister_power_state_listener();
 cleanup_hcd:
 	mausb_deinit_hcd();
 cleanup:
@@ -91,6 +100,8 @@ static int mausb_host_init(void)
 static void mausb_host_exit(void)
 {
 	mausb_pr_info("Module unloading started...");
+	mausb_unregister_power_state_listener();
+	mausb_deinitialize_mss();
 	mausb_deinit_hcd();
 	mausb_cleanup_dev(1);
 	mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION);
diff --git a/drivers/usb/mausb_host/mausb_event.h b/drivers/usb/mausb_host/mausb_event.h
new file mode 100644
index 000000000000..636c07b5f2be
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_event.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_EVENT_H__
+#define __MAUSB_MAUSB_EVENT_H__
+
+#include "ma_usb.h"
+
+#define MAUSB_MAX_NUM_OF_MA_DEVS			15
+#define MAUSB_RING_BUFFER_SIZE				1024
+#define MAUSB_MAX_DATA_IN_REQ_SIZE			28
+
+#define MAUSB_EVENT_TYPE_DEV_RESET			1u
+#define MAUSB_EVENT_TYPE_USB_DEV_HANDLE			2u
+#define MAUSB_EVENT_TYPE_EP_HANDLE			3u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE		4u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE		5u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_RESET		6u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_DELETE		7u
+#define MAUSB_EVENT_TYPE_MODIFY_EP0			8u
+#define MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS		9u
+#define MAUSB_EVENT_TYPE_UPDATE_DEV			10u
+#define MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT		11u
+#define MAUSB_EVENT_TYPE_PING				12u
+#define MAUSB_EVENT_TYPE_DEV_DISCONNECT			13u
+#define MAUSB_EVENT_TYPE_USB_DEV_RESET			14u
+#define MAUSB_EVENT_TYPE_CANCEL_TRANSFER		15u
+
+#define MAUSB_EVENT_TYPE_PORT_CHANGED			80u
+#define MAUSB_EVENT_TYPE_SEND_MGMT_MSG			81u
+#define MAUSB_EVENT_TYPE_SEND_DATA_MSG			82u
+#define MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG		83u
+#define MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG		84u
+#define MAUSB_EVENT_TYPE_URB_COMPLETE			85u
+#define MAUSB_EVENT_TYPE_SEND_ACK			86u
+#define MAUSB_EVENT_TYPE_ITERATOR_RESET			87u
+#define MAUSB_EVENT_TYPE_ITERATOR_SEEK			88u
+#define MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER		89u
+#define MAUSB_EVENT_TYPE_DELETE_MA_DEV			90u
+#define MAUSB_EVENT_TYPE_USER_FINISHED			91u
+#define MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES	92u
+#define MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED		93u
+#define MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT		94u
+
+#define MAUSB_EVENT_TYPE_NONE				255u
+
+#define MAUSB_DATA_MSG_DIRECTION_OUT			0
+#define MAUSB_DATA_MSG_DIRECTION_IN			1
+#define MAUSB_DATA_MSG_CONTROL MAUSB_DATA_MSG_DIRECTION_OUT
+
+struct mausb_devhandle {
+	u64 event_id;
+	u32 route_string;
+	u16 hub_dev_handle;
+	u16 parent_hs_hub_dev_handle;
+	u16 parent_hs_hub_port;
+	u16 mtt;
+	/* dev_handle assigned in user */
+	u16 dev_handle;
+	u8  device_speed;
+	u8  lse;
+};
+
+struct mausb_ephandle {
+	u64 event_id;
+	u16 device_handle;
+	u16 descriptor_size;
+	/* ep_handle assigned in user */
+	u16 ep_handle;
+	char	 descriptor[sizeof(struct ma_usb_ephandlereq_desc_ss)];
+};
+
+struct mausb_epactivate {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_epinactivate {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_epreset {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+	u8  tsp;
+};
+
+struct mausb_epdelete {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_updatedev {
+	u64 event_id;
+	u16 device_handle;
+	u16 max_exit_latency;
+	struct ma_usb_updatedevreq_desc update_descriptor;
+	u8  hub;
+	u8  number_of_ports;
+	u8  mtt;
+	u8  ttt;
+	u8  integrated_hub_latency;
+};
+
+struct mausb_usbdevreset {
+	u64 event_id;
+	u16 device_handle;
+};
+
+struct mausb_modifyep0 {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+	__le16 max_packet_size;
+};
+
+struct mausb_setusbdevaddress {
+	u64 event_id;
+	u16 device_handle;
+	u16 response_timeout;
+};
+
+struct mausb_usbdevdisconnect {
+	u16 device_handle;
+};
+
+struct mausb_canceltransfer {
+	u64 urb;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_mgmt_hdr {
+	__aligned(4) char hdr[MAUSB_MAX_MGMT_SIZE];
+};
+
+struct mausb_mgmt_req_timedout {
+	u64 event_id;
+};
+
+struct mausb_delete_ma_dev {
+	u64 event_id;
+	u16 device_id;
+};
+
+/* TODO split mgmt_event to generic send mgmt req and specific requests */
+struct mausb_mgmt_event {
+	union {
+		struct mausb_devhandle		dev_handle;
+		struct mausb_ephandle		ep_handle;
+		struct mausb_epactivate		ep_activate;
+		struct mausb_epinactivate	ep_inactivate;
+		struct mausb_epreset		ep_reset;
+		struct mausb_epdelete		ep_delete;
+		struct mausb_modifyep0		modify_ep0;
+		struct mausb_setusbdevaddress	set_usb_dev_address;
+		struct mausb_updatedev		update_dev;
+		struct mausb_usbdevreset	usb_dev_reset;
+		struct mausb_usbdevdisconnect	usb_dev_disconnect;
+		struct mausb_canceltransfer	cancel_transfer;
+		struct mausb_mgmt_hdr		mgmt_hdr;
+		struct mausb_mgmt_req_timedout	mgmt_req_timedout;
+		struct mausb_delete_ma_dev	delete_ma_dev;
+	};
+};
+
+struct mausb_data_event {
+	u64 urb;
+	u64 recv_buf;
+	u32 iterator_seek_delta;
+	u32 transfer_size;
+	u32 rem_transfer_size;
+	u32 transfer_flags;
+	u32 isoch_seg_num;
+	u32 req_id;
+	u32 payload_size;
+	s32 status;
+
+	__aligned(4) char hdr[MAUSB_TRANSFER_HDR_SIZE];
+	__aligned(4) char hdr_ack[MAUSB_TRANSFER_HDR_SIZE];
+
+	u16 device_id;
+	u16 ep_handle;
+	u16 packet_size;
+	u8  setup_packet;
+	u8  direction;
+	u8  transfer_type;
+	u8  first_control_packet;
+	u8  transfer_eot;
+	u8  mausb_address;
+	u8  mausb_ssid;
+};
+
+struct mausb_port_changed_event {
+	u8 port;
+	u8 dev_type;
+	u8 dev_speed;
+	u8 lse;
+};
+
+struct mausb_event {
+	union {
+		struct mausb_mgmt_event		mgmt;
+		struct mausb_data_event		data;
+		struct mausb_port_changed_event port_changed;
+	};
+	s32 status;
+	u8 type;
+	u8 madev_addr;
+};
+
+struct mausb_events_notification {
+	u16 num_of_events;
+	u16 num_of_completed_events;
+	u8  madev_addr;
+};
+
+#endif /* __MAUSB_MAUSB_EVENT_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
                     ` (4 preceding siblings ...)
  2020-03-27 15:26   ` [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-27 15:26   ` [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemeneted connection between PAL and Link layers and set up
environment for exchanging PAL-to-PAL messages.

Within this patch, driver's sysfs parameters have been created
with intention to configure remote connection parameters.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/hpal.c       | 522 ++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal.h       |   6 +
 drivers/usb/mausb_host/mausb_core.c | 103 +++++-
 3 files changed, 630 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index ac86cbc71e36..b8e00e6ef69c 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -13,10 +13,15 @@
 #include "hcd.h"
 #include "utils.h"
 
+#define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
+
 struct mss mss;
 
+static int mausb_start_connection_timer(struct mausb_device *dev);
 static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
 				void *data);
+static void mausb_signal_empty_mss(void);
+static void mausb_remove_madev_from_list(u8 madev_addr);
 static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
 static int mausb_start_heartbeat_timer(void);
 
@@ -177,6 +182,55 @@ static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx,
 	return true;
 }
 
+static void mausb_complete_urbs_from_tree(void)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+	struct urb	     *current_urb = NULL;
+	struct rb_node	     *current_node = NULL;
+	unsigned long flags;
+	int status = 0;
+	int ret;
+
+	mausb_pr_debug("Completing all urbs from tree");
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	while ((current_node = rb_first(&mhcd->mausb_urbs))) {
+		urb_ctx = rb_entry(current_node, struct mausb_urb_ctx, rb_node);
+
+		current_urb = urb_ctx->urb;
+		mausb_delete_urb_ctx_from_tree(urb_ctx);
+		mausb_uninit_data_iterator(&urb_ctx->iterator);
+		kfree(urb_ctx);
+
+		ret = usb_hcd_check_unlink_urb(current_urb->hcpriv,
+					       current_urb, status);
+		if (ret == -EIDRM)
+			mausb_pr_warn("Urb=%p is already unlinked",
+				      current_urb);
+		else
+			usb_hcd_unlink_urb_from_ep(current_urb->hcpriv,
+						   current_urb);
+
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+
+		/* Prepare urb for completion */
+		mausb_pr_debug("Completing urb=%p", current_urb);
+
+		current_urb->status	   = -EPROTO;
+		current_urb->actual_length = 0;
+		atomic_dec(&current_urb->use_count);
+		usb_hcd_giveback_urb(current_urb->hcpriv, current_urb,
+				     current_urb->status);
+
+		spin_lock_irqsave(&mhcd->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	mausb_pr_debug("Completed all urbs from tree");
+}
+
 /*After this function call only valid thing to do with urb is to give it back*/
 struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
 							    int status)
@@ -284,6 +338,161 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev,
 		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+static void mausb_socket_disconnect_event(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						socket_disconnect_work);
+	struct mausb_event event;
+
+	mausb_pr_info("madev_addr=%d", dev->madev_addr);
+
+	mausb_ip_disconnect(dev->ctrl_channel);
+	mausb_destroy_ip_ctx(dev->ctrl_channel);
+	dev->ctrl_channel = NULL;
+
+	mausb_ip_disconnect(dev->bulk_channel);
+	mausb_destroy_ip_ctx(dev->bulk_channel);
+	dev->bulk_channel = NULL;
+
+	mausb_ip_disconnect(dev->isoch_channel);
+	mausb_destroy_ip_ctx(dev->isoch_channel);
+	dev->isoch_channel = NULL;
+
+	if (dev->mgmt_channel) {
+		memset(&event, 0, sizeof(event));
+		event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
+		event.data.device_id = dev->id;
+
+		mausb_pr_info("Releasing MAUSB device ref");
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+	}
+
+	mausb_ip_disconnect(dev->mgmt_channel);
+	mausb_destroy_ip_ctx(dev->mgmt_channel);
+	dev->mgmt_channel = NULL;
+
+	memset(dev->channel_map, 0, sizeof(dev->channel_map));
+}
+
+static void mausb_disconnect_ma_dev(struct mausb_device *dev)
+{
+	mausb_pr_info("Disconnecting MAUSB device madev_addr=%d",
+		      dev->madev_addr);
+
+	if (!dev->dev_connected) {
+		mausb_pr_warn("MAUSB device is not connected");
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return;
+	}
+	mausb_hcd_disconnect(dev->port_number, dev->dev_type, dev->dev_speed);
+
+	if (dev->dev_type == USB30HUB)
+		mausb_hcd_disconnect(dev->port_number, USB20HUB, HIGH_SPEED);
+}
+
+static void mausb_hcd_disconnect_event(struct work_struct *work)
+{
+	struct mausb_device *ma_dev = container_of(work, struct mausb_device,
+						   hcd_disconnect_work);
+
+	mausb_disconnect_ma_dev(ma_dev);
+}
+
+static void mausb_delete_madev(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						madev_delete_work);
+	struct mausb_event	event;
+	struct completion	completion;
+	struct completion	*user_event;
+	struct mausb_completion mausb_completion;
+	long status;
+	unsigned long timeout = msecs_to_jiffies(MAUSB_DELETE_MADEV_TIMEOUT_MS);
+
+	mausb_pr_info("Deleting MAUSB device madev_addr=%d", dev->madev_addr);
+
+	del_timer_sync(&dev->connection_timer);
+
+	/* Client IS responsive */
+	if (!atomic_read(&dev->unresponsive_client)) {
+		memset(&event, 0, sizeof(event));
+		event.type = MAUSB_EVENT_TYPE_DELETE_MA_DEV;
+		event.mgmt.delete_ma_dev.device_id = dev->id;
+		event.mgmt.delete_ma_dev.event_id  = mausb_event_id(dev);
+
+		init_completion(&completion);
+		mausb_completion.completion_event = &completion;
+		mausb_completion.event_id = event.mgmt.delete_ma_dev.event_id;
+		mausb_completion.mausb_event = &event;
+
+		mausb_insert_event(dev, &mausb_completion);
+
+		mausb_pr_debug("Deleting MAUSB device...");
+
+		status = wait_for_completion_interruptible_timeout(&completion,
+								   timeout);
+
+		mausb_pr_debug("Deleting MAUSB device event finished with %ld",
+			       status);
+
+		mausb_remove_event(dev, &mausb_completion);
+
+		user_event = &dev->user_finished_event;
+
+		status = wait_for_completion_interruptible_timeout(user_event,
+								   timeout);
+		mausb_pr_info("User event finished with %ld", status);
+	}
+
+	flush_workqueue(dev->workq);
+	destroy_workqueue(dev->workq);
+
+	mausb_clear_hcd_madev(dev->port_number);
+
+	mausb_remove_madev_from_list(dev->madev_addr);
+
+	put_net(dev->net_ns);
+
+	kfree(dev);
+	mausb_signal_empty_mss();
+
+	mausb_pr_info("MAUSB device deleted. Version=%s", MAUSB_DRIVER_VERSION);
+}
+
+static void mausb_ping_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						ping_work);
+
+	if (mausb_start_connection_timer(dev) < 0) {
+		mausb_pr_err("Device disconnecting due to session timeout madev_addr=%d",
+			     dev->madev_addr);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return;
+	}
+}
+
+static void mausb_heartbeat_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						heartbeat_work);
+
+	mausb_pr_err("Device disconnecting - app is unresponsive");
+	atomic_set(&dev->unresponsive_client, 1);
+	mausb_complete_urbs_from_tree();
+	queue_work(dev->workq, &dev->socket_disconnect_work);
+	queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
+
+static void mausb_connection_timer_func(struct timer_list *timer)
+{
+	struct mausb_device *dev = container_of(timer, struct mausb_device,
+						connection_timer);
+
+	queue_work(dev->workq, &dev->ping_work);
+}
+
 static void mausb_heartbeat_timer_func(struct timer_list *timer)
 {
 	unsigned long flags = 0;
@@ -308,6 +517,99 @@ static void mausb_heartbeat_timer_func(struct timer_list *timer)
 	}
 }
 
+static struct mausb_device *
+mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
+		   int *status)
+{
+	struct mausb_device *dev;
+	unsigned long flags = 0;
+	char workq_name[16];
+	struct workqueue_struct *workq;
+
+	memset(workq_name, 0, sizeof(workq_name));
+	sprintf(workq_name, "%x", madev_address);
+	strcat(workq_name, "_madev_workq");
+
+	mausb_pr_debug("madev_workq_name = %s", workq_name);
+
+	workq = alloc_ordered_workqueue(workq_name, WQ_MEM_RECLAIM);
+	if (!workq) {
+		mausb_pr_alert("Could not allocate workqueue!");
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	if (mss.deinit_in_progress) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		mausb_pr_alert("Device creating failed - mss deinit in progress");
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -ESHUTDOWN;
+		return NULL;
+	}
+
+	dev = mausb_get_dev_from_addr_unsafe(madev_address);
+	if (dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		mausb_pr_debug("MAUSB device already connected, madev_address=%x",
+			       madev_address);
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -EEXIST;
+		return NULL;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		mausb_pr_alert("Could not allocate MAUSB device!");
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	mausb_pr_info("Create MAUSB device. Version=%s", MAUSB_DRIVER_VERSION);
+
+	dev->workq = workq;
+
+	INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event);
+	INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event);
+	INIT_WORK(&dev->madev_delete_work, mausb_delete_madev);
+	INIT_WORK(&dev->ping_work, mausb_ping_work);
+	INIT_WORK(&dev->heartbeat_work, mausb_heartbeat_work);
+
+	kref_init(&dev->refcount);
+
+	dev->event_id = 0;
+	spin_lock_init(&dev->event_id_lock);
+
+	INIT_LIST_HEAD(&dev->completion_events);
+	spin_lock_init(&dev->completion_events_lock);
+	spin_lock_init(&dev->num_of_user_events_lock);
+	spin_lock_init(&dev->connection_timer_lock);
+
+	init_completion(&dev->user_finished_event);
+	atomic_set(&dev->unresponsive_client, 0);
+
+	timer_setup(&dev->connection_timer, mausb_connection_timer_func, 0);
+
+	dev->dev_addr = dev_addr;
+	dev->madev_addr = madev_address;
+	dev->net_ns = get_net(current->nsproxy->net_ns);
+
+	list_add_tail(&dev->list_entry, &mss.madev_list);
+
+	reinit_completion(&mss.empty);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return dev;
+}
+
 void mausb_release_ma_dev_async(struct kref *kref)
 {
 	struct mausb_device *dev = container_of(kref, struct mausb_device,
@@ -318,6 +620,45 @@ void mausb_release_ma_dev_async(struct kref *kref)
 	schedule_work(&dev->madev_delete_work);
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
+				  u8 madev_address)
+{
+	int error = 0;
+	struct mausb_device *dev;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(madev_address);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	if (dev) {
+		mausb_pr_debug("MAUSB device already connected, madev_address=%x",
+			       madev_address);
+		return -EEXIST;
+	}
+
+	dev = mausb_create_madev(dev_addr, madev_address, &error);
+
+	if (!dev)
+		return error;
+
+	mausb_pr_info("New MAUSB device created madev_addr=%d", madev_address);
+
+	error = mausb_init_ip_ctx(&dev->mgmt_channel, dev->net_ns,
+				  dev->dev_addr.ip.address,
+				  dev->dev_addr.ip.port.management, dev,
+				  mausb_ip_callback, MAUSB_MGMT_CHANNEL);
+	if (error) {
+		mausb_pr_err("Mgmt ip context init failed: error=%d", error);
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return error;
+	}
+
+	mausb_ip_connect_async(dev->mgmt_channel);
+
+	return 0;
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 				  u16 num_of_completed)
 {
@@ -418,6 +759,26 @@ int mausb_signal_event(struct mausb_device *dev,
 	return -ETIMEDOUT;
 }
 
+static int mausb_start_connection_timer(struct mausb_device *dev)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&dev->connection_timer_lock, flags);
+
+	if (++dev->receive_failures_num > MAUSB_MAX_RECEIVE_FAILURES) {
+		mausb_pr_err("Missed more than %d ping responses",
+			     MAUSB_MAX_RECEIVE_FAILURES);
+		spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+	spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+
+	return 0;
+}
+
 void mausb_reset_connection_timer(struct mausb_device *dev)
 {
 	unsigned long flags = 0;
@@ -661,6 +1022,36 @@ struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr)
 	return NULL;
 }
 
+static void mausb_remove_madev_from_list(u8 madev_addr)
+{
+	unsigned long flags = 0;
+	struct mausb_device *ma_dev, *tmp = NULL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	list_for_each_entry_safe(ma_dev, tmp, &mss.madev_list, list_entry) {
+		if (ma_dev->madev_addr == madev_addr) {
+			list_del(&ma_dev->list_entry);
+			break;
+		}
+	}
+
+	if (list_empty(&mss.madev_list))
+		reinit_completion(&mss.rings_events.mausb_ring_has_events);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_signal_empty_mss(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (list_empty(&mss.madev_list))
+		complete(&mss.empty);
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
 static inline
 struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
 					    enum mausb_channel channel)
@@ -814,6 +1205,137 @@ void mausb_cleanup_chunks_list(struct list_head *chunks_list)
 	}
 }
 
+static void mausb_init_ip_ctx_helper(struct mausb_device *dev,
+				     struct mausb_ip_ctx **ip_ctx,
+				     u16 port,
+				     enum mausb_channel channel)
+{
+	int status = mausb_init_ip_ctx(ip_ctx, dev->net_ns,
+				       dev->dev_addr.ip.address, port, dev,
+				       mausb_ip_callback, channel);
+	if (status < 0) {
+		mausb_pr_err("Init ip context failed with error=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	dev->channel_map[channel] = *ip_ctx;
+	mausb_ip_connect_async(*ip_ctx);
+}
+
+static void mausb_connect_callback(struct mausb_device *dev,
+				   enum mausb_channel channel, int status)
+{
+	struct mausb_device_address *dev_addr = &dev->dev_addr;
+
+	mausb_pr_info("Connect callback for channel=%d with status=%d",
+		      channel, status);
+
+	if (status < 0) {
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	if (channel == MAUSB_MGMT_CHANNEL) {
+		if (dev_addr->ip.port.control == 0) {
+			dev->channel_map[MAUSB_CTRL_CHANNEL] =
+				dev->mgmt_channel;
+			channel = MAUSB_CTRL_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->ctrl_channel,
+						 dev_addr->ip.port.control,
+						 MAUSB_CTRL_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_CTRL_CHANNEL) {
+		if (dev_addr->ip.port.bulk == 0) {
+			dev->channel_map[MAUSB_BULK_CHANNEL] =
+				dev->channel_map[MAUSB_CTRL_CHANNEL];
+			channel = MAUSB_BULK_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->bulk_channel,
+						 dev_addr->ip.port.bulk,
+						 MAUSB_BULK_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_BULK_CHANNEL) {
+		if (dev_addr->ip.port.isochronous == 0) {
+			/* if there is no isoch port use tcp for it */
+			dev->channel_map[MAUSB_ISOCH_CHANNEL] =
+				dev->channel_map[MAUSB_BULK_CHANNEL];
+			channel = MAUSB_ISOCH_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->isoch_channel,
+						 dev_addr->ip.port.isochronous,
+						 MAUSB_ISOCH_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_ISOCH_CHANNEL) {
+		dev->channel_map[MAUSB_INTR_CHANNEL] =
+				dev->channel_map[MAUSB_CTRL_CHANNEL];
+	}
+}
+
+static void mausb_handle_connect_event(struct mausb_device *dev,
+				       enum mausb_channel channel, int status,
+				       void *data)
+{
+	mausb_connect_callback(dev, channel, status);
+}
+
+static void mausb_handle_receive_event(struct mausb_device *dev,
+				       enum mausb_channel channel, int status,
+				       void *data)
+{
+	struct mausb_event event;
+
+	event.madev_addr = dev->madev_addr;
+
+	if (status <= 0) {
+		mausb_pr_err("Receive event error status=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return;
+	}
+
+	mausb_reset_connection_timer(dev);
+}
+
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+		       enum mausb_link_action action, int status, void *data)
+{
+	struct mausb_device *dev = (struct mausb_device *)ctx;
+
+	switch (action) {
+	case MAUSB_LINK_CONNECT:
+		mausb_handle_connect_event(dev, channel, status, data);
+		break;
+	case MAUSB_LINK_SEND:
+		/*
+		 * Currently there is nothing to do, as send operation is
+		 * synchronous
+		 */
+		break;
+	case MAUSB_LINK_RECV:
+		mausb_handle_receive_event(dev, channel, status, data);
+		break;
+	case MAUSB_LINK_DISCONNECT:
+		/*
+		 * Currently there is nothing to do, as disconnect operation is
+		 * synchronous
+		 */
+		break;
+	default:
+		mausb_pr_warn("Unknown network action");
+	}
+}
+
 static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
 				     u32 byte_num,
 				     struct list_head *data_chunks_list,
diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h
index f184bbc07783..a04ed120ba5e 100644
--- a/drivers/usb/mausb_host/hpal.h
+++ b/drivers/usb/mausb_host/hpal.h
@@ -131,6 +131,8 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
 	return val;
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address device_address,
+				  u8 madev_address);
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request);
 int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
@@ -158,6 +160,7 @@ static inline void mausb_remove_event(struct mausb_device *dev,
 }
 
 void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_on_madev_connected(struct mausb_device *dev);
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
 void mausb_complete_urb(struct mausb_event *event);
 void mausb_reset_connection_timer(struct mausb_device *dev);
@@ -240,6 +243,9 @@ enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type)
 	return transfer_type >> 3;
 }
 
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+		       enum mausb_link_action action, int status, void *data);
+
 struct mausb_data_iter {
 	u32 length;
 
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
index 8730590126ea..101afd0b9deb 100644
--- a/drivers/usb/mausb_host/mausb_core.c
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -13,41 +13,122 @@
 
 #include "hcd.h"
 #include "hpal.h"
+#include "mausb_address.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("DisplayLink (UK) Ltd.");
 MODULE_VERSION(MAUSB_DRIVER_VERSION);
 
+static struct mausb_device_address	device_address;
+static int				mausb_device_disconnect_param;
+static u16				madev_addr;
+static u8				mausb_client_connect_param;
+static u8				mausb_client_disconnect_param;
+
 static int mausb_client_connect(const char *value,
 				const struct kernel_param *kp)
 {
+	unsigned long flags = 0;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
+	spin_lock_irqsave(&mss.lock, flags);
+	if (mss.client_connected) {
+		mausb_pr_err("MA-USB client is already connected");
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -EEXIST;
+	}
+	/* Save heartbeat client information */
+	mss.client_connected = true;
+	mss.missed_heartbeats = 0;
+	reinit_completion(&mss.client_stopped);
+	spin_unlock_irqrestore(&mss.lock, flags);
+	/* Start hearbeat timer */
+	mod_timer(&mss.heartbeat_timer,
+		  jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
 	return 0;
 }
 
 static int mausb_client_disconnect(const char *value,
 				   const struct kernel_param *kp)
 {
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
+	spin_lock_irqsave(&mss.lock, flags);
+	if (!mss.client_connected) {
+		mausb_pr_err("MA-USB client is not connected");
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -ENODEV;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	/* Stop heartbeat timer */
+	del_timer_sync(&mss.heartbeat_timer);
+
+	/* Clear heartbeat client information */
+	spin_lock_irqsave(&mss.lock, flags);
+	mss.client_connected = false;
+	mss.missed_heartbeats = 0;
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x",
+			       dev->madev_addr);
+		queue_work(dev->workq, &dev->heartbeat_work);
+	}
+	complete(&mss.client_stopped);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
 	return 0;
 }
 
 static int mausb_device_connect(const char *value,
 				const struct kernel_param *kp)
 {
+	int status = 0;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
-	return 0;
+	if (strlen(value) <= INET6_ADDRSTRLEN) {
+		strcpy(device_address.ip.address, value);
+		mausb_pr_info("Processing '%s' address",
+			      device_address.ip.address);
+	} else {
+		mausb_pr_err("Invalid IP format");
+		return 0;
+	}
+	status = mausb_initiate_dev_connection(device_address, madev_addr);
+	memset(&device_address, 0, sizeof(device_address));
+
+	return status;
 }
 
 static int mausb_device_disconnect(const char *value,
 				   const struct kernel_param *kp)
 {
+	u8 dev_address = 0;
+	int status = 0;
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
+	status = kstrtou8(value, 0, &dev_address);
+	if (status < 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	dev = mausb_get_dev_from_addr_unsafe(dev_address);
+	if (dev)
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
 	return 0;
 }
 
@@ -67,6 +148,26 @@ static const struct kernel_param_ops mausb_client_disconnect_ops = {
 	.set = mausb_client_disconnect
 };
 
+module_param_named(mgmt, device_address.ip.port.management, ushort, 0664);
+MODULE_PARM_DESC(mgmt, "MA-USB management port");
+module_param_named(ctrl, device_address.ip.port.control, ushort, 0664);
+MODULE_PARM_DESC(ctrl, "MA-USB control port");
+module_param_named(bulk, device_address.ip.port.bulk, ushort, 0664);
+MODULE_PARM_DESC(bulk, "MA-USB bulk port");
+module_param_named(isoch, device_address.ip.port.isochronous, ushort, 0664);
+MODULE_PARM_DESC(isoch, "MA-USB isochronous port");
+module_param_named(madev_addr, madev_addr, ushort, 0664);
+MODULE_PARM_DESC(madev_addr, "MA-USB device address");
+
+module_param_cb(client_connect, &mausb_client_connect_ops,
+		&mausb_client_connect_param, 0664);
+module_param_cb(client_disconnect, &mausb_client_disconnect_ops,
+		&mausb_client_disconnect_param, 0664);
+module_param_cb(ip, &mausb_device_connect_ops,
+		device_address.ip.address, 0664);
+module_param_cb(disconnect, &mausb_device_disconnect_ops,
+		&mausb_device_disconnect_param, 0664);
+
 static int mausb_host_init(void)
 {
 	int status;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
                     ` (5 preceding siblings ...)
  2020-03-27 15:26   ` [PATCH v4 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-03-28 10:35     ` kbuild test robot
  2020-04-04 16:07     ` kbuild test robot
  2020-03-27 15:26   ` [PATCH v4 8/8] usb: mausb_host: Process MA-USB data packets vladimir.stankovic
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
  8 siblings, 2 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented MA-USB management messages processing and communication
with user-space driver via mapped memory.

MA USB PAL events are being enqueued into ring buffer from which
data is being send/received to/from user-space.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile              |   1 +
 drivers/usb/mausb_host/hcd.c                 | 252 +++++++-
 drivers/usb/mausb_host/hpal.c                | 449 +++++++++++++-
 drivers/usb/mausb_host/hpal.h                |  44 ++
 drivers/usb/mausb_host/hpal_events.c         | 611 +++++++++++++++++++
 drivers/usb/mausb_host/hpal_events.h         |  85 +++
 drivers/usb/mausb_host/mausb_driver_status.h |  17 +
 drivers/usb/mausb_host/utils.c               | 275 +++++++++
 drivers/usb/mausb_host/utils.h               |   5 +
 9 files changed, 1729 insertions(+), 10 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal_events.c
 create mode 100644 drivers/usb/mausb_host/hpal_events.h
 create mode 100644 drivers/usb/mausb_host/mausb_driver_status.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index 829314b15cbb..fd2a36a04ad6 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -11,5 +11,6 @@ mausb_host-y += utils.o
 mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
+mausb_host-y += hpal_events.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
index 70cb633c39ba..8cf885612684 100644
--- a/drivers/usb/mausb_host/hcd.c
+++ b/drivers/usb/mausb_host/hcd.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 
 #include "hpal.h"
+#include "hpal_events.h"
 #include "utils.h"
 
 static int mausb_open(struct inode *inode, struct file *file);
@@ -1075,6 +1076,18 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 	}
 
 	if (ep_ctx) {
+		status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
+							  ep_ctx->ep_handle);
+
+		mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d",
+			      ep_ctx->ep_handle, dev_handle, status);
+
+		status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
+						      ep_ctx->ep_handle);
+
+		if (status < 0)
+			mausb_pr_warn("ep_handle_del request failed for ep0: ep_handle=%#x, dev_handle=%#x",
+				      ep_ctx->ep_handle, dev_handle);
 		dev->ep0.hcpriv = NULL;
 		kfree(ep_ctx);
 
@@ -1082,6 +1095,14 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle);
 	}
 
+	if (dev_handle != DEV_HANDLE_NOT_ASSIGNED) {
+		status = mausb_usbdevdisconnect_event_to_user(ma_dev,
+							      dev_handle);
+		if (status < 0)
+			mausb_pr_warn("usb_dev_disconnect req failed for dev_handle=%#x",
+				      dev_handle);
+	}
+
 free_dev:
 	if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
 		mausb_pr_info("All usb devices destroyed - proceed with disconnecting");
@@ -1096,6 +1117,21 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_clear_hcd_madev(port_number);
 }
 
+static int mausb_device_assign_address(struct mausb_device *dev,
+				       struct mausb_usb_device_ctx *usb_dev_ctx)
+{
+	int status =
+		mausb_setusbdevaddress_event_to_user(dev,
+						     usb_dev_ctx->dev_handle,
+						     RESPONSE_TIMEOUT);
+
+	mausb_pr_info("dev_handle=%#x, status=%d", usb_dev_ctx->dev_handle,
+		      status);
+	usb_dev_ctx->addressed = (status == 0);
+
+	return status;
+}
+
 static struct mausb_usb_device_ctx *
 mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
 		       struct mausb_device *ma_dev, u16 port_number,
@@ -1182,6 +1218,12 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 			return status;
 	}
 
+	if (!usb_device_ctx->addressed) {
+		status = mausb_device_assign_address(ma_dev, usb_device_ctx);
+		if (status < 0)
+			return status;
+	}
+
 	endpoint_ctx = dev->ep0.hcpriv;
 	if (!endpoint_ctx) {
 		mausb_pr_err("endpoint_ctx is NULL: dev_handle=%#x",
@@ -1189,7 +1231,23 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	return 0;
+	/*
+	 * Fix to support usb 2.0 logitech camera. If endoint handle of usb 2.0
+	 * device is already modified, do not modify it again.
+	 */
+	if (dev->speed < USB_SPEED_SUPER && endpoint_ctx->ep_handle != 0)
+		return 0;
+
+	status = mausb_modifyep0_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       &endpoint_ctx->ep_handle,
+					       dev->ep0.desc.wMaxPacketSize);
+
+	mausb_pr_info("modify ep0 response, ep_handle=%#x, dev_handle=%#x, status=%d",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		      status);
+
+	return status;
 }
 
 static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
@@ -1242,10 +1300,32 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
 						    &endpoint->desc,
 						    &endpoint->ss_ep_comp);
+		status = mausb_ephandle_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      sizeof(descriptor_ss),
+						      &descriptor_ss,
+						      &endpoint_ctx->ep_handle);
+
 	} else {
 		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+		status = mausb_ephandle_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      sizeof(descriptor),
+						      &descriptor,
+						      &endpoint_ctx->ep_handle);
 	}
 
+	if (status < 0) {
+		mausb_pr_err("ep_handle_request failed dev_handle=%#x",
+			     usb_dev_ctx->dev_handle);
+		endpoint->hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return status;
+	}
+
+	mausb_pr_info("Endpoint added ep_handle=%#x, dev_handle=%#x",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
 	return 0;
 }
 
@@ -1254,6 +1334,7 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	u8	port_number;
 	int	status;
+	int	retries	    = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
@@ -1288,9 +1369,49 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return -ENODEV;
 	}
 
+	mausb_pr_info("Start dropping ep_handle=%#x, dev_handle=%#x",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - drop endpoint immediately");
+		endpoint->hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return -ESHUTDOWN;
+	}
+
+	status = mausb_epinactivate_event_to_user(ma_dev,
+						  usb_dev_ctx->dev_handle,
+						  endpoint_ctx->ep_handle);
+
+	mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		      status);
+
+	while (true) {
+		status = mausb_epdelete_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      endpoint_ctx->ep_handle);
+
+		mausb_pr_info("ep_handle_delete_request, ep_handle=%#x, dev_handle=%#x, status=%d",
+			      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+			      status);
+
+		if (status == -EBUSY) {
+			if (++retries < MAUSB_BUSY_RETRIES_COUNT)
+				usleep_range(10000, 10001);
+			else
+				return -EBUSY;
+		} else {
+			break;
+		}
+	}
+
+	mausb_pr_info("Endpoint dropped ep_handle=%#x, dev_handle=%#x",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
 	endpoint->hcpriv = NULL;
 	kfree(endpoint_ctx);
-	return 0;
+	return status;
 }
 
 static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
@@ -1343,6 +1464,20 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
 		return -EINVAL;
 	}
 
+	status = mausb_usbdevhandle_event_to_user(ma_dev, (u8)dev_speed,
+						  dev->route, hub_dev_handle,
+						  parent_hs_hub_dev_handle,
+						  parent_hs_hub_port, 0,
+						  ma_dev->lse,
+						  &usb_device_ctx->dev_handle);
+
+	mausb_pr_info("mausb_usbdevhandle_event status=%d", status);
+
+	if (status < 0)
+		return status;
+
+	mausb_pr_info("Get ref dev_handle=%#x", usb_device_ctx->dev_handle);
+
 	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
 	if (!endpoint_ctx)
 		return -ENOMEM;
@@ -1354,6 +1489,22 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
 
 	mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
 
+	status = mausb_ephandle_event_to_user(ma_dev,
+					      usb_device_ctx->dev_handle,
+					      sizeof(descriptor),
+					      &descriptor,
+					      &endpoint_ctx->ep_handle);
+
+	mausb_pr_info("mausb_ephandle_event ep_handle=%#x, dev_handle=%#x, status=%d",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		      status);
+
+	if (status < 0) {
+		dev->ep0.hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return status;
+	}
+
 	return 0;
 }
 
@@ -1396,9 +1547,21 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 			return status;
 	}
 
+	mausb_pr_info("Device assigned and addressed usb_device_ctx=%p",
+		      usb_device_ctx);
+
 	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
 		return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
 						      usb_device_ctx);
+
+	/*
+	 * Fix for usb 2.0 logitech camera
+	 */
+	if (!usb_device_ctx->addressed)
+		return mausb_device_assign_address(ma_dev, usb_device_ctx);
+
+	mausb_pr_info("Device assigned and addressed dev_handle=%#x",
+		      usb_device_ctx->dev_handle);
 	return 0;
 }
 
@@ -1444,7 +1607,15 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -ENODEV;
 	}
 
-	return 0;
+	status = mausb_updatedev_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       0, 0, 0, 0, 0, 0,
+					       &dev->descriptor);
+
+	mausb_pr_info("Finished dev_handle=%#x, status=%d",
+		      usb_device_ctx->dev_handle, status);
+
+	return status;
 }
 
 static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
@@ -1454,8 +1625,10 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 	u8	port_number;
 	unsigned long flags;
 	u16 max_exit_latency = 0;
+	u8  number_of_ports = (u8)dev->maxchild;
 	u8  mtt = 0;
 	u8  ttt = 0;
+	u8  integrated_hub_latency = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
@@ -1495,7 +1668,17 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 	else if (dev->usb3_lpm_u2_enabled)
 		max_exit_latency = (u16)dev->u2_params.mel;
 
-	return 0;
+	status = mausb_updatedev_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       max_exit_latency, 1,
+					       number_of_ports, mtt, ttt,
+					       integrated_hub_latency,
+					       &dev->descriptor);
+
+	mausb_pr_info("Finished dev_handle=%#x, status=%d",
+		      usb_device_ctx->dev_handle, status);
+
+	return status;
 }
 
 static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
@@ -1531,6 +1714,8 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct usb_device	    *dev;
 	struct mausb_endpoint_ctx   *ep_ctx;
+	struct ma_usb_ephandlereq_desc_ss  descriptor_ss;
+	struct ma_usb_ephandlereq_desc_std descriptor;
 
 	ep_ctx = endpoint->hcpriv;
 	if (!ep_ctx) {
@@ -1565,6 +1750,15 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	is_out = usb_endpoint_dir_out(&endpoint->desc);
 	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
 
+	status = mausb_epreset_event_to_user(ma_dev, dev_handle,
+					     ep_ctx->ep_handle, tsp);
+
+	mausb_pr_info("ep_reset status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
 	if (status != EUCLEAN) {
 		if (!tsp) {
 			usb_settoggle(dev, epnum, is_out, 0U);
@@ -1572,12 +1766,52 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 				usb_settoggle(dev, epnum, !is_out, 0U);
 		}
 
+		status = mausb_epactivate_event_to_user(ma_dev, dev_handle,
+							ep_ctx->ep_handle);
+
+		mausb_pr_err("ep_activate failed, status=%d, ep_handle=%#x, dev_handle=%#x",
+			     status, ep_ctx->ep_handle, dev_handle);
+
 		return;
 	}
 
 	if (tsp)
 		return;
 
+	status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
+						  ep_ctx->ep_handle);
+
+	mausb_pr_info("ep_inactivate status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
+	status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
+					      ep_ctx->ep_handle);
+
+	mausb_pr_info("ep_handle_delete status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
+	if (dev->speed >= USB_SPEED_SUPER) {
+		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
+						    &endpoint->desc,
+						    &endpoint->ss_ep_comp);
+		status = mausb_ephandle_event_to_user(ma_dev, dev_handle,
+						      sizeof(descriptor_ss),
+						      &descriptor_ss,
+						      &ep_ctx->ep_handle);
+	} else {
+		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+		status = mausb_ephandle_event_to_user(ma_dev, dev_handle,
+						      sizeof(descriptor),
+						      &descriptor,
+						      &ep_ctx->ep_handle);
+	}
+
 	mausb_pr_info("ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x",
 		      status, ep_ctx->ep_handle, dev_handle);
 }
@@ -1624,7 +1858,15 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 
 	dev_handle = usb_device_ctx->dev_handle;
 
-	return 0;
+	status = mausb_usbdevreset_event_to_user(ma_dev, dev_handle);
+
+	mausb_pr_info("usb_dev_reset dev_handle=%#x, status=%d",
+		      dev_handle, status);
+
+	if (status == 0)
+		usb_device_ctx->addressed = false;
+
+	return status;
 }
 
 void mausb_clear_hcd_madev(u16 port_number)
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index b8e00e6ef69c..1e7bbe3b230a 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -11,6 +11,7 @@
 #include <linux/uio.h>
 
 #include "hcd.h"
+#include "hpal_events.h"
 #include "utils.h"
 
 #define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
@@ -278,6 +279,31 @@ void mausb_release_event_resources(struct mausb_event *event)
 	kfree(receive_buffer);
 }
 
+static void mausb_iterator_reset(struct mausb_device *dev,
+				 struct mausb_event *event)
+{
+	struct urb	     *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+
+	urb_ctx = mausb_find_urb_in_tree(urb);
+
+	if (urb_ctx)
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+}
+
+static void mausb_iterator_seek(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	struct urb	     *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+
+	urb_ctx = mausb_find_urb_in_tree(urb);
+
+	if (urb_ctx)
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 event->data.iterator_seek_delta);
+}
+
 void mausb_complete_urb(struct mausb_event *event)
 {
 	struct urb *urb = (struct urb *)event->data.urb;
@@ -291,6 +317,46 @@ void mausb_complete_urb(struct mausb_event *event)
 			       event->status);
 }
 
+static void mausb_delete_ma_dev(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	mausb_signal_event(dev, event, event->mgmt.delete_ma_dev.event_id);
+}
+
+static void mausb_process_user_finished(struct mausb_device *dev,
+					struct mausb_event *event)
+{
+	complete(&dev->user_finished_event);
+}
+
+static int mausb_send_mgmt_msg(struct mausb_device *dev,
+			       struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper wrapper;
+	struct kvec kvec;
+	struct ma_usb_hdr_common *hdr;
+	int status;
+
+	hdr = (struct ma_usb_hdr_common *)event->mgmt.mgmt_hdr.hdr;
+
+	mausb_pr_info("event=%d, type=%d", event->type, hdr->type);
+
+	kvec.iov_base	 = hdr;
+	kvec.iov_len	 = hdr->length;
+	wrapper.kvec	 = &kvec;
+	wrapper.kvec_num = 1;
+	wrapper.length	 = hdr->length;
+
+	status = mausb_ip_send(dev->mgmt_channel, &wrapper);
+	if (status < 0) {
+		mausb_pr_err("Send failed. Disconnecting... status=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	return status;
+}
+
 static int mausb_get_first_free_port_number(u16 *port_number)
 {
 	(*port_number) = 0;
@@ -338,11 +404,142 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev,
 		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+static void mausb_complete_timeout_event(struct mausb_device *dev,
+					 struct mausb_event *event)
+{
+	mausb_pr_debug("Event type=%d, event_id=%llu", event->type,
+		       event->mgmt.mgmt_req_timedout.event_id);
+	mausb_signal_event(dev, event, event->mgmt.mgmt_req_timedout.event_id);
+}
+
+static void mausb_process_event(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	mausb_pr_debug("Event type=%d", event->type);
+
+	switch (event->type) {
+	case MAUSB_EVENT_TYPE_USB_DEV_HANDLE:
+		mausb_usbdevhandle_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE:
+		mausb_ephandle_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE:
+		mausb_epactivate_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE:
+		mausb_epinactivate_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_RESET:
+		mausb_epreset_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_DELETE:
+		mausb_epdelete_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_MODIFY_EP0:
+		mausb_modifyep0_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS:
+		mausb_setusbdevaddress_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_UPDATE_DEV:
+		mausb_updatedev_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_USB_DEV_RESET:
+		mausb_usbdevreset_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_CANCEL_TRANSFER:
+		mausb_canceltransfer_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_PORT_CHANGED:
+		mausb_port_has_changed_event(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_PING:
+		mausb_send_mgmt_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_MGMT_MSG:
+		mausb_send_mgmt_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_DATA_MSG:
+		mausb_send_data_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG:
+		mausb_receive_data_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_URB_COMPLETE:
+		mausb_complete_urb(event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_ACK:
+		mausb_send_transfer_ack(dev, event);
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_ITERATOR_RESET:
+		mausb_iterator_reset(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_ITERATOR_SEEK:
+		mausb_iterator_seek(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_DELETE_MA_DEV:
+		mausb_delete_ma_dev(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_USER_FINISHED:
+		mausb_process_user_finished(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES:
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_NONE:
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT:
+		mausb_complete_timeout_event(dev, event);
+		break;
+	default:
+		break;
+	}
+
+	mausb_notify_completed_user_events(dev->ring_buffer);
+}
+
+static void mausb_hpal_kernel_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						work);
+	struct mausb_event *event;
+	int status;
+	u16 i;
+	u16 events;
+	u16 completed_events;
+	unsigned long flags;
+	struct mausb_ring_buffer *dev_mausb_ring = dev->ring_buffer;
+
+	spin_lock_irqsave(&dev->num_of_user_events_lock, flags);
+	events = dev->num_of_user_events;
+	completed_events = dev->num_of_completed_events;
+	dev->num_of_user_events = 0;
+	dev->num_of_completed_events = 0;
+	spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags);
+
+	status = mausb_ring_buffer_move_tail(dev_mausb_ring, completed_events);
+	if (status < 0) {
+		mausb_pr_err("Dequeue failed, status=%d", status);
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return;
+	}
+
+	for (i = 0; i < events; ++i) {
+		event = mausb_ring_current_from_user(dev_mausb_ring);
+		mausb_ring_next_from_user(dev_mausb_ring);
+		mausb_process_event(dev, event);
+	}
+}
+
 static void mausb_socket_disconnect_event(struct work_struct *work)
 {
 	struct mausb_device *dev = container_of(work, struct mausb_device,
 						socket_disconnect_work);
 	struct mausb_event event;
+	int status;
 
 	mausb_pr_info("madev_addr=%d", dev->madev_addr);
 
@@ -363,6 +560,11 @@ static void mausb_socket_disconnect_event(struct work_struct *work)
 		event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
 		event.data.device_id = dev->id;
 
+		status = mausb_enqueue_event_to_user(dev, &event);
+
+		mausb_pr_info("Sending notification to user that network is disconnected status=%d",
+			      status);
+
 		mausb_pr_info("Releasing MAUSB device ref");
 		kref_put(&dev->refcount, mausb_release_ma_dev_async);
 	}
@@ -427,6 +629,13 @@ static void mausb_delete_madev(struct work_struct *work)
 
 		mausb_insert_event(dev, &mausb_completion);
 
+		status = mausb_enqueue_event_to_user(dev, &event);
+		if (status < 0) {
+			mausb_remove_event(dev, &mausb_completion);
+			mausb_pr_err("Ring buffer full, enqueue failed");
+			return;
+		}
+
 		mausb_pr_debug("Deleting MAUSB device...");
 
 		status = wait_for_completion_interruptible_timeout(&completion,
@@ -449,10 +658,14 @@ static void mausb_delete_madev(struct work_struct *work)
 
 	mausb_clear_hcd_madev(dev->port_number);
 
+	mausb_ring_buffer_cleanup(dev->ring_buffer);
+	mausb_ring_buffer_destroy(dev->ring_buffer);
+
 	mausb_remove_madev_from_list(dev->madev_addr);
 
 	put_net(dev->net_ns);
 
+	kfree(dev->ring_buffer);
 	kfree(dev);
 	mausb_signal_empty_mss();
 
@@ -463,6 +676,7 @@ static void mausb_ping_work(struct work_struct *work)
 {
 	struct mausb_device *dev = container_of(work, struct mausb_device,
 						ping_work);
+	int status = 0;
 
 	if (mausb_start_connection_timer(dev) < 0) {
 		mausb_pr_err("Device disconnecting due to session timeout madev_addr=%d",
@@ -471,6 +685,13 @@ static void mausb_ping_work(struct work_struct *work)
 		queue_work(dev->workq, &dev->hcd_disconnect_work);
 		return;
 	}
+
+	status = mausb_ping_event_to_user(dev);
+
+	if (status < 0) {
+		mausb_pr_err("Ring buffer full");
+		return;
+	}
 }
 
 static void mausb_heartbeat_work(struct work_struct *work)
@@ -576,6 +797,7 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
 
 	dev->workq = workq;
 
+	INIT_WORK(&dev->work, mausb_hpal_kernel_work);
 	INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event);
 	INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event);
 	INIT_WORK(&dev->madev_delete_work, mausb_delete_madev);
@@ -601,6 +823,15 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
 	dev->madev_addr = madev_address;
 	dev->net_ns = get_net(current->nsproxy->net_ns);
 
+	if (!list_empty(&mss.available_ring_buffers)) {
+		dev->ring_buffer = container_of(mss.available_ring_buffers.next,
+						struct mausb_ring_buffer,
+						list_entry);
+		list_del(mss.available_ring_buffers.next);
+	} else {
+		mausb_pr_alert("Ring buffer for mausb device is not availbale!");
+	}
+
 	list_add_tail(&dev->list_entry, &mss.madev_list);
 
 	reinit_completion(&mss.empty);
@@ -659,6 +890,14 @@ int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
 	return 0;
 }
 
+void mausb_on_madev_connected(struct mausb_device *dev)
+{
+	struct mausb_event mausb_event;
+
+	mausb_dev_reset_req_event(&mausb_event);
+	mausb_enqueue_event_to_user(dev, &mausb_event);
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 				  u16 num_of_completed)
 {
@@ -683,9 +922,29 @@ int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 	return 0;
 }
 
+int mausb_enqueue_event_to_user(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	int status;
+
+	event->madev_addr = dev->madev_addr;
+	status = mausb_ring_buffer_put(dev->ring_buffer, event);
+	if (status < 0) {
+		mausb_pr_err("Ring buffer operation failed");
+		mausb_cleanup_ring_buffer_event(event);
+		return status;
+	}
+
+	mausb_notify_ring_events(dev->ring_buffer);
+	mausb_pr_debug("User-space notification sent.");
+
+	return 0;
+}
+
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request)
 {
+	int status;
 	struct mausb_event mausb_event;
 
 	mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
@@ -728,7 +987,12 @@ int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 		       &request->dev->route, sizeof(request->dev->route));
 	}
 
-	return 0;
+	status = mausb_enqueue_event_to_user(dev, &mausb_event);
+	if (status < 0)
+		mausb_pr_err("Failed to enqueue event to user-space ep_handle=%#x, status=%d",
+			     mausb_event.data.ep_handle, status);
+
+	return status;
 }
 
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status)
@@ -841,6 +1105,17 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
 	mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x",
 		       urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
 
+	if (!usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+		status = mausb_canceltransfer_event_to_user(ep_ctx->ma_dev,
+							    ep_ctx->dev_handle,
+							    ep_ctx->ep_handle,
+							    (u64)urb);
+		if (status < 0) {
+			mausb_pr_err("Failed to enqueue cancel transfer to user");
+			goto complete_urb;
+		}
+	}
+
 	memset(&mausb_event, 0, sizeof(mausb_event));
 
 	mausb_event.type   = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER;
@@ -855,6 +1130,13 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
 						MAUSB_DATA_MSG_DIRECTION_IN :
 						MAUSB_DATA_MSG_DIRECTION_OUT);
 
+	status = mausb_enqueue_event_to_user(ep_ctx->ma_dev, &mausb_event);
+	if (status < 0) {
+		mausb_pr_alert("Failed to enqueue event to user-space ep_handle=%#x, status=%d",
+			       mausb_event.data.ep_handle, status);
+		goto complete_urb;
+	}
+
 	if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) {
 		mausb_pr_alert("Failed to insert in tree urb=%p ep_handle=%#x, status=%d",
 			       urb, mausb_event.data.ep_handle, status);
@@ -916,6 +1198,7 @@ void mausb_deinitialize_mss(void)
 
 	wait_for_completion(&mss.empty);
 	mausb_pr_debug("Waiting for completion on disconnect_event ended");
+	mausb_stop_ring_events();
 
 	timeout = wait_for_completion_timeout(&mss.client_stopped, timeout);
 	mausb_pr_info("Remaining time after waiting for stopping client %ld",
@@ -1104,7 +1387,6 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
 int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 {
 	struct mausb_urb_ctx *urb_ctx;
-	int status = 0;
 
 	if (event->status != 0) {
 		mausb_pr_err("Event %d failed with status %d",
@@ -1119,10 +1401,9 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled for event=%d",
 			      event->type);
-		return status;
 	}
 
-	return status;
+	return 0;
 }
 
 int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
@@ -1142,8 +1423,10 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 
 	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
 
-	if (!urb_ctx)
+	if (!urb_ctx) {
+		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled");
+	}
 
 cleanup:
 	mausb_release_event_resources(event);
@@ -1279,6 +1562,7 @@ static void mausb_connect_callback(struct mausb_device *dev,
 	if (channel == MAUSB_ISOCH_CHANNEL) {
 		dev->channel_map[MAUSB_INTR_CHANNEL] =
 				dev->channel_map[MAUSB_CTRL_CHANNEL];
+		mausb_on_madev_connected(dev);
 	}
 }
 
@@ -1305,6 +1589,16 @@ static void mausb_handle_receive_event(struct mausb_device *dev,
 	}
 
 	mausb_reset_connection_timer(dev);
+
+	status = mausb_msg_received_event(&event,
+					  (struct ma_usb_hdr_common *)data,
+					  channel);
+
+	if (status == 0)
+		status = mausb_enqueue_event_to_user(dev, &event);
+
+	if (status < 0)
+		mausb_pr_err("Failed to enqueue, status=%d", status);
 }
 
 void mausb_ip_callback(void *ctx, enum mausb_channel channel,
@@ -1614,3 +1908,148 @@ u32 mausb_data_iterator_length(struct mausb_data_iter *iterator)
 {
 	return iterator->length;
 }
+
+static int mausb_ring_buffer_get(struct mausb_ring_buffer *ring,
+				 struct mausb_event *event)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 1) {
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	memcpy(event, ring->to_user_buffer + ring->tail, sizeof(*event));
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	ring->tail = (ring->tail + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+int mausb_ring_buffer_init(struct mausb_ring_buffer *ring)
+{
+	unsigned int page_order =
+		mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+				     sizeof(struct mausb_event));
+	ring->to_user_buffer =
+		(struct mausb_event *)__get_free_pages(GFP_KERNEL, page_order);
+	if (!ring->to_user_buffer)
+		return -ENOMEM;
+	ring->from_user_buffer = ring->to_user_buffer + MAUSB_RING_BUFFER_SIZE;
+	ring->head = 0;
+	ring->tail = 0;
+	ring->current_from_user = 0;
+	ring->buffer_full = false;
+	spin_lock_init(&ring->lock);
+
+	return 0;
+}
+
+int mausb_ring_buffer_put(struct mausb_ring_buffer *ring,
+			  struct mausb_event *event)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+
+	if (ring->buffer_full) {
+		mausb_pr_err("Ring buffer is full");
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+
+	if (CIRC_SPACE(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 2) {
+		mausb_pr_err("Ring buffer capacity exceeded, disconnecting device");
+		ring->buffer_full = true;
+		mausb_disconect_event_unsafe(ring, event->madev_addr);
+		ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	memcpy(ring->to_user_buffer + ring->head, event, sizeof(*event));
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < count) {
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	mausb_pr_debug("old HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	ring->tail = (ring->tail + (int)count) & (MAUSB_RING_BUFFER_SIZE - 1);
+	mausb_pr_debug("new HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring)
+{
+	struct mausb_event event;
+
+	while (mausb_ring_buffer_get(ring, &event) == 0)
+		mausb_cleanup_ring_buffer_event(&event);
+}
+
+void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring)
+{
+	unsigned int page_order =
+		mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+				     sizeof(struct mausb_event));
+	if (ring && ring->to_user_buffer)
+		free_pages((unsigned long)ring->to_user_buffer, page_order);
+}
+
+void mausb_cleanup_ring_buffer_event(struct mausb_event *event)
+{
+	mausb_pr_debug("event=%d", event->type);
+	switch (event->type) {
+	case MAUSB_EVENT_TYPE_SEND_DATA_MSG:
+		mausb_cleanup_send_data_msg_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG:
+		mausb_cleanup_received_data_msg_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER:
+		mausb_cleanup_delete_data_transfer_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_NONE:
+		break;
+	default:
+		mausb_pr_warn("Unknown event type");
+		break;
+	}
+}
+
+void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring,
+				  uint8_t madev_addr)
+{
+	struct mausb_event disconnect_event;
+	struct mausb_device *dev = mausb_get_dev_from_addr_unsafe(madev_addr);
+
+	if (!dev) {
+		mausb_pr_err("Device not found, madev_addr=%#x", madev_addr);
+		return;
+	}
+
+	disconnect_event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
+	disconnect_event.status = -EINVAL;
+	disconnect_event.madev_addr = madev_addr;
+
+	memcpy(ring->to_user_buffer + ring->head, &disconnect_event,
+	       sizeof(disconnect_event));
+
+	mausb_pr_info("Disconnect event added to ring buffer for madev_addr=%#x",
+		      madev_addr);
+	mausb_pr_info("Adding hcd_disconnect_work to dev workq, madev_addr=%#x",
+		      madev_addr);
+	queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h
index a04ed120ba5e..24846d4bc4a3 100644
--- a/drivers/usb/mausb_host/hpal.h
+++ b/drivers/usb/mausb_host/hpal.h
@@ -67,6 +67,7 @@ struct mss {
 struct mausb_device {
 	struct mausb_device_address dev_addr;
 	struct net		    *net_ns;
+	struct mausb_ring_buffer    *ring_buffer;
 	struct list_head	    list_entry;
 
 	struct mausb_ip_ctx *mgmt_channel;
@@ -133,6 +134,10 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
 
 int mausb_initiate_dev_connection(struct mausb_device_address device_address,
 				  u8 madev_address);
+int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
+				  u16 num_of_completed);
+int mausb_enqueue_event_to_user(struct mausb_device *dev,
+				struct mausb_event *event);
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request);
 int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
@@ -282,6 +287,33 @@ void mausb_reset_data_iterator(struct mausb_data_iter *iterator);
 void mausb_uninit_data_iterator(struct mausb_data_iter *iterator);
 void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta);
 
+struct mausb_ring_buffer {
+	atomic_t mausb_ring_events;
+	atomic_t mausb_completed_user_events;
+
+	struct mausb_event *to_user_buffer;
+	int		head;
+	int		tail;
+	spinlock_t	lock; /* Protect ring buffer */
+	u64		id;
+
+	struct mausb_event *from_user_buffer;
+	int current_from_user;
+
+	struct list_head list_entry;
+	bool buffer_full;
+};
+
+int mausb_ring_buffer_init(struct mausb_ring_buffer *ring);
+int mausb_ring_buffer_put(struct mausb_ring_buffer *ring,
+			  struct mausb_event *event);
+int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count);
+void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring);
+void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring);
+void mausb_cleanup_ring_buffer_event(struct mausb_event *event);
+void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring,
+				  uint8_t madev_addr);
+
 static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
 						unsigned int elem_size)
 {
@@ -292,4 +324,16 @@ static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
 	return order;
 }
 
+static inline
+struct mausb_event *mausb_ring_current_from_user(struct mausb_ring_buffer *ring)
+{
+	return ring->from_user_buffer + ring->current_from_user;
+}
+
+static inline void mausb_ring_next_from_user(struct mausb_ring_buffer *ring)
+{
+	ring->current_from_user = (ring->current_from_user + 1) &
+				  (MAUSB_RING_BUFFER_SIZE - 1);
+}
+
 #endif /* __MAUSB_HPAL_H__ */
diff --git a/drivers/usb/mausb_host/hpal_events.c b/drivers/usb/mausb_host/hpal_events.c
new file mode 100644
index 000000000000..6bec951213ea
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_events.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_events.h"
+
+#include <linux/slab.h>
+
+#include "hcd.h"
+#include "utils.h"
+
+void mausb_dev_reset_req_event(struct mausb_event *event)
+{
+	event->type = MAUSB_EVENT_TYPE_DEV_RESET;
+}
+
+static int mausb_mgmt_msg_received_event(struct mausb_event *event,
+					 struct ma_usb_hdr_common *hdr,
+					 enum mausb_channel channel)
+{
+	int status = 0;
+
+	mausb_pr_info("channel=%d, type=%d", channel, hdr->type);
+	if (hdr->length <= MAUSB_MAX_MGMT_SIZE) {
+		event->type = MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG;
+		memcpy(event->mgmt.mgmt_hdr.hdr, hdr, hdr->length);
+	} else {
+		mausb_pr_err("MGMT message to long, failed to copy");
+		status = -EINVAL;
+	}
+
+	kfree(hdr);
+	return status;
+}
+
+static int mausb_data_msg_received_event(struct mausb_event *event,
+					 struct ma_usb_hdr_common *hdr,
+					 enum mausb_channel channel)
+{
+	event->type		  = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
+	event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
+	event->data.device_id	  = (u16)((hdr->ssid << 8) | hdr->dev_addr);
+	event->data.ep_handle	  = hdr->handle.epv;
+	event->data.recv_buf	  = (u64)hdr;
+
+	memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
+
+	if (mausb_ctrl_transfer(hdr) &&
+	    hdr->length <= 2 * MAUSB_TRANSFER_HDR_SIZE) {
+		memcpy(event->data.hdr_ack,
+		       shift_ptr(hdr, MAUSB_TRANSFER_HDR_SIZE),
+		       (size_t)(hdr->length - MAUSB_TRANSFER_HDR_SIZE));
+	}
+
+	return 0;
+}
+
+static int mausb_isoch_msg_received_event(struct mausb_event *event,
+					  struct ma_usb_hdr_common *hdr,
+					  enum mausb_channel channel)
+{
+	event->type		  = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
+	event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
+	event->data.device_id	  = (u16)((hdr->ssid << 8) | hdr->dev_addr);
+	event->data.ep_handle	  = hdr->handle.epv;
+	event->data.recv_buf	  = (u64)hdr;
+
+	memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
+
+	return 0;
+}
+
+int mausb_msg_received_event(struct mausb_event *event,
+			     struct ma_usb_hdr_common *hdr,
+			     enum mausb_channel channel)
+{
+	mausb_pr_debug("channel=%d, type=%d", channel, hdr->type);
+	if (mausb_is_management_hdr_type(hdr->type))
+		return mausb_mgmt_msg_received_event(event, hdr, channel);
+	else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(TRANSFER))
+		return mausb_data_msg_received_event(event, hdr, channel);
+	else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(ISOCHTRANSFER))
+		return mausb_isoch_msg_received_event(event, hdr, channel);
+
+	kfree(hdr);
+	mausb_pr_warn("Unknown event type event=%d", hdr->type);
+	return -EBADR;
+}
+
+static void mausb_prepare_completion(struct mausb_completion *mausb_completion,
+				     struct completion *completion,
+				     struct mausb_event *event, u64 event_id)
+{
+	init_completion(completion);
+
+	mausb_completion->completion_event = completion;
+	mausb_completion->event_id	   = event_id;
+	mausb_completion->mausb_event	   = event;
+}
+
+static int mausb_wait_for_completion(struct mausb_event *event, u64 event_id,
+				     struct mausb_device *dev)
+{
+	struct completion	completion;
+	struct mausb_completion mausb_completion;
+	long status;
+	unsigned long timeout;
+
+	mausb_prepare_completion(&mausb_completion, &completion, event,
+				 event_id);
+	mausb_insert_event(dev, &mausb_completion);
+
+	status = mausb_enqueue_event_to_user(dev, event);
+	if (status < 0) {
+		mausb_remove_event(dev, &mausb_completion);
+		mausb_pr_err("Ring buffer full, event_id=%lld", event_id);
+		return (int)status;
+	}
+
+	timeout = msecs_to_jiffies(MANAGEMENT_EVENT_TIMEOUT);
+	status = wait_for_completion_interruptible_timeout(&completion,
+							   timeout);
+
+	mausb_remove_event(dev, &mausb_completion);
+
+	if (status == 0) {
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
+				     u8 device_speed,
+				     u32 route_string,
+				     u16 hub_dev_handle,
+				     u16 parent_hs_hub_dev_handle,
+				     u16 parent_hs_hub_port, u16 mtt,
+				     u8 lse, s32 *usb_dev_handle)
+{
+	struct mausb_event event;
+	int status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_USB_DEV_HANDLE;
+	event.mgmt.dev_handle.device_speed	 = device_speed;
+	event.mgmt.dev_handle.route_string	 = route_string;
+	event.mgmt.dev_handle.hub_dev_handle	 = hub_dev_handle;
+	event.mgmt.dev_handle.parent_hs_hub_port = parent_hs_hub_port;
+	event.mgmt.dev_handle.mtt		 = mtt;
+	event.mgmt.dev_handle.lse		 = lse;
+	event.mgmt.dev_handle.event_id		 = event_id;
+	event.madev_addr			 = dev->madev_addr;
+	event.mgmt.dev_handle.parent_hs_hub_dev_handle =
+						   parent_hs_hub_dev_handle;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Usbdevhandle failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*usb_dev_handle = event.mgmt.dev_handle.dev_handle;
+
+	return 0;
+}
+
+int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.dev_handle.event_id);
+}
+
+int mausb_ephandle_event_to_user(struct mausb_device *dev,
+				 u16 device_handle,
+				 u16 descriptor_size, void *descriptor,
+				 u16 *ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			     = MAUSB_EVENT_TYPE_EP_HANDLE;
+	event.mgmt.ep_handle.device_handle   = device_handle;
+	event.mgmt.ep_handle.descriptor_size = descriptor_size;
+	event.mgmt.ep_handle.event_id	     = event_id;
+	event.madev_addr		     = dev->madev_addr;
+
+	memcpy(event.mgmt.ep_handle.descriptor, descriptor, descriptor_size);
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Ephandle failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*ep_handle = event.mgmt.ep_handle.ep_handle;
+
+	return 0;
+}
+
+int mausb_ephandle_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_handle.event_id);
+}
+
+int mausb_epactivate_event_to_user(struct mausb_device *dev,
+				   u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE;
+	event.mgmt.ep_activate.device_handle = device_handle;
+	event.mgmt.ep_activate.ep_handle     = ep_handle;
+	event.mgmt.ep_activate.event_id	     = event_id;
+	event.madev_addr		     = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epactivate failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epactivate_event_from_user(struct mausb_device *dev,
+				     struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+		event->mgmt.ep_activate.event_id);
+}
+
+int mausb_epinactivate_event_to_user(struct mausb_device *dev,
+				     u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE;
+	event.mgmt.ep_inactivate.device_handle	= device_handle;
+	event.mgmt.ep_inactivate.ep_handle	= ep_handle;
+	event.mgmt.ep_inactivate.event_id	= event_id;
+	event.madev_addr			= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epinactivate failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epinactivate_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+				  event->mgmt.ep_inactivate.event_id);
+}
+
+int mausb_epreset_event_to_user(struct mausb_device *dev,
+				u16 device_handle, u16 ep_handle,
+				u8 tsp_flag)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			  = MAUSB_EVENT_TYPE_EP_HANDLE_RESET;
+	event.mgmt.ep_reset.device_handle = device_handle;
+	event.mgmt.ep_reset.ep_handle	  = ep_handle;
+	event.mgmt.ep_reset.tsp		  = tsp_flag;
+	event.mgmt.ep_reset.event_id	  = event_id;
+	event.madev_addr		  = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epreset failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epreset_event_from_user(struct mausb_device *dev,
+				  struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_reset.event_id);
+}
+
+int mausb_epdelete_event_to_user(struct mausb_device *dev,
+				 u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			   = MAUSB_EVENT_TYPE_EP_HANDLE_DELETE;
+	event.mgmt.ep_delete.device_handle = device_handle;
+	event.mgmt.ep_delete.ep_handle	   = ep_handle;
+	event.mgmt.ep_delete.event_id	   = event_id;
+	event.madev_addr		   = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epdelete failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epdelete_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_delete.event_id);
+}
+
+int mausb_modifyep0_event_to_user(struct mausb_device *dev,
+				  u16 device_handle, u16 *ep_handle,
+				  __le16 max_packet_size)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type				= MAUSB_EVENT_TYPE_MODIFY_EP0;
+	event.mgmt.modify_ep0.device_handle	= device_handle;
+	event.mgmt.modify_ep0.ep_handle		= *ep_handle;
+	event.mgmt.modify_ep0.max_packet_size	= max_packet_size;
+	event.mgmt.modify_ep0.event_id		= event_id;
+	event.madev_addr			= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Modifyep0 failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*ep_handle = event.mgmt.modify_ep0.ep_handle;
+
+	return 0;
+}
+
+int mausb_modifyep0_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.modify_ep0.event_id);
+}
+
+int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
+					 u16 device_handle,
+					 u16 response_timeout)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS;
+	event.mgmt.set_usb_dev_address.device_handle	= device_handle;
+	event.mgmt.set_usb_dev_address.response_timeout	= response_timeout;
+	event.mgmt.set_usb_dev_address.event_id		= event_id;
+	event.madev_addr				= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Setusbdevaddress failed, event_id=%lld",
+			     event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
+					   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+		event->mgmt.set_usb_dev_address.event_id);
+}
+
+static void
+mausb_init_device_descriptor(struct ma_usb_updatedevreq_desc *update_descriptor,
+			     struct usb_device_descriptor *device_descriptor)
+{
+	update_descriptor->usb20.bLength = device_descriptor->bLength;
+	update_descriptor->usb20.bDescriptorType =
+					device_descriptor->bDescriptorType;
+	update_descriptor->usb20.bcdUSB = device_descriptor->bcdUSB;
+	update_descriptor->usb20.bDeviceClass =
+					device_descriptor->bDeviceClass;
+	update_descriptor->usb20.bDeviceSubClass =
+					device_descriptor->bDeviceSubClass;
+	update_descriptor->usb20.bDeviceProtocol =
+					device_descriptor->bDeviceProtocol;
+	update_descriptor->usb20.bMaxPacketSize0 =
+					device_descriptor->bMaxPacketSize0;
+	update_descriptor->usb20.idVendor  = device_descriptor->idVendor;
+	update_descriptor->usb20.idProduct = device_descriptor->idProduct;
+	update_descriptor->usb20.bcdDevice = device_descriptor->bcdDevice;
+	update_descriptor->usb20.iManufacturer =
+					device_descriptor->iManufacturer;
+	update_descriptor->usb20.iProduct  = device_descriptor->iProduct;
+	update_descriptor->usb20.iSerialNumber =
+					device_descriptor->iSerialNumber;
+	update_descriptor->usb20.bNumConfigurations =
+					device_descriptor->bNumConfigurations;
+}
+
+int mausb_updatedev_event_to_user(struct mausb_device *dev,
+				  u16 device_handle,
+				  u16 max_exit_latency, u8 hub,
+				  u8 number_of_ports, u8 mtt,
+				  u8 ttt, u8 integrated_hub_latency,
+				  struct usb_device_descriptor *dev_descriptor)
+{
+	struct mausb_event event;
+	int status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_UPDATE_DEV;
+	event.mgmt.update_dev.device_handle	     = device_handle;
+	event.mgmt.update_dev.max_exit_latency	     = max_exit_latency;
+	event.mgmt.update_dev.hub		     = hub;
+	event.mgmt.update_dev.number_of_ports	     = number_of_ports;
+	event.mgmt.update_dev.mtt		     = mtt;
+	event.mgmt.update_dev.ttt		     = ttt;
+	event.mgmt.update_dev.integrated_hub_latency = integrated_hub_latency;
+	event.mgmt.update_dev.event_id		     = event_id;
+	event.madev_addr			     = dev->madev_addr;
+
+	mausb_init_device_descriptor(&event.mgmt.update_dev.update_descriptor,
+				     dev_descriptor);
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Updatedev failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_updatedev_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.update_dev.event_id);
+}
+
+int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
+					 u16 dev_handle)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type = MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT;
+	event.mgmt.usb_dev_disconnect.device_handle = dev_handle;
+	event.madev_addr			    = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		mausb_pr_err("Ring buffer full, usbdevdisconnect failed");
+
+	return status;
+}
+
+int mausb_ping_event_to_user(struct mausb_device *dev)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type	 = MAUSB_EVENT_TYPE_PING;
+	event.madev_addr = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		mausb_pr_err("Ring buffer full, devdisconnect failed");
+
+	return status;
+}
+
+__attribute__((unused))
+static int mausb_devdisconnect_event_to_user(struct mausb_device *dev)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type	 = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
+	event.madev_addr = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		mausb_pr_err("Ring buffer full, devdisconnect failed");
+
+	return status;
+}
+
+int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
+				    u16 device_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			       = MAUSB_EVENT_TYPE_USB_DEV_RESET;
+	event.mgmt.usb_dev_reset.device_handle = device_handle;
+	event.mgmt.usb_dev_reset.event_id      = event_id;
+	event.madev_addr		       = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Usbdevreset failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
+				      struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+				  event->mgmt.usb_dev_reset.event_id);
+}
+
+int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
+				       u16 device_handle,
+				       u16 ep_handle, u64 urb)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type = MAUSB_EVENT_TYPE_CANCEL_TRANSFER;
+	event.mgmt.cancel_transfer.device_handle = device_handle;
+	event.mgmt.cancel_transfer.ep_handle	 = ep_handle;
+	event.mgmt.cancel_transfer.urb		 = urb;
+	event.madev_addr			 = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0) {
+		mausb_pr_err("Ring buffer full, canceltransfer failed");
+		return status;
+	}
+
+	return status;
+}
+
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event)
+{
+	mausb_pr_debug("");
+	return 0;
+}
+
+void mausb_cleanup_send_data_msg_event(struct mausb_event *event)
+{
+	mausb_complete_urb(event);
+}
+
+void mausb_cleanup_received_data_msg_event(struct mausb_event *event)
+{
+	mausb_release_event_resources(event);
+}
+
+void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
+
+	urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+	if (!urb_ctx) {
+		mausb_pr_warn("Urb=%p is not in tree", urb);
+		return;
+	}
+
+	/* Deallocate urb_ctx */
+	mausb_uninit_data_iterator(&urb_ctx->iterator);
+	kfree(urb_ctx);
+
+	urb->status = -EPROTO;
+	urb->actual_length = 0;
+	atomic_dec(&urb->use_count);
+	usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+}
diff --git a/drivers/usb/mausb_host/hpal_events.h b/drivers/usb/mausb_host/hpal_events.h
new file mode 100644
index 000000000000..fb424d526b12
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_events.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_EVENTS_H__
+#define __MAUSB_HPAL_EVENTS_H__
+
+#include "hpal.h"
+#include "ip_link.h"
+#include "mausb_event.h"
+
+#define MANAGEMENT_EVENT_TIMEOUT 3000
+
+int mausb_msg_received_event(struct mausb_event *event,
+			     struct ma_usb_hdr_common *hdr,
+			     enum mausb_channel channel);
+int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
+				     u8 device_speed,
+				     u32 route_string,
+				     u16 hub_dev_handle,
+				     u16 parent_hs_hub_dev_handle,
+				     u16 parent_hs_hub_port, u16 mtt,
+				     u8 lse, s32 *usb_dev_handle);
+int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event);
+int mausb_ephandle_event_to_user(struct mausb_device *dev, u16 device_handle,
+				 u16 descriptor_size, void *descriptor,
+				 u16 *ep_handle);
+int mausb_ephandle_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event);
+int mausb_epactivate_event_to_user(struct mausb_device *dev,
+				   u16 device_handle, u16 ep_handle);
+int mausb_epactivate_event_from_user(struct mausb_device *dev,
+				     struct mausb_event *event);
+int mausb_epinactivate_event_to_user(struct mausb_device *dev,
+				     u16 device_handle,
+				     u16 ep_handle);
+int mausb_epinactivate_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event);
+int mausb_epreset_event_to_user(struct mausb_device *dev,
+				u16 device_handle, u16 ep_handle,
+				u8 tsp_flag);
+int mausb_epreset_event_from_user(struct mausb_device *dev,
+				  struct mausb_event *event);
+int mausb_epdelete_event_to_user(struct mausb_device *dev,
+				 u16 device_handle, u16 ep_handle);
+int mausb_epdelete_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event);
+int mausb_modifyep0_event_to_user(struct mausb_device *dev,
+				  u16 device_handle, u16 *ep_handle,
+				  __le16 max_packet_size);
+int mausb_modifyep0_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event);
+int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
+					 u16 device_handle,
+					 u16 response_timeout);
+int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
+					   struct mausb_event *event);
+int mausb_updatedev_event_to_user(struct mausb_device *dev,
+				  u16 device_handle,
+				  u16 max_exit_latency, u8 hub,
+				  u8 number_of_ports, u8 mtt,
+				  u8 ttt, u8 integrated_hub_latency,
+				  struct usb_device_descriptor *dev_descriptor);
+int mausb_updatedev_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event);
+int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
+					 u16 dev_handle);
+int mausb_ping_event_to_user(struct mausb_device *dev);
+int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
+				    u16 device_handle);
+int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
+				      struct mausb_event *event);
+int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
+				       u16 device_handle,
+				       u16 ep_handle, u64 urb);
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event);
+
+void mausb_dev_reset_req_event(struct mausb_event *event);
+void mausb_cleanup_send_data_msg_event(struct mausb_event *event);
+void mausb_cleanup_received_data_msg_event(struct mausb_event *event);
+void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_EVENTS_H__ */
diff --git a/drivers/usb/mausb_host/mausb_driver_status.h b/drivers/usb/mausb_host/mausb_driver_status.h
new file mode 100644
index 000000000000..9b55befe9cae
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_driver_status.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_DRIVER_STATUS_H__
+#define __MAUSB_MAUSB_DRIVER_STATUS_H__
+
+#define MAUSB_DRIVER_BAD_READ_BUFFER_SIZE  -1
+#define MAUSB_DRIVER_RING_EVENTS_STOPPED   -2
+#define MAUSB_DRIVER_SYSTEM_SUSPENDED      -3
+#define MAUSB_DRIVER_COPY_TO_BUFFER_FAILED -4
+
+#define MAUSB_DRIVER_READ_TIMEOUT 0
+#define MAUSB_DRIVER_READ_ERROR  -1
+#define MAUSB_DRIVER_WRITE_ERROR  -2
+
+#endif /* __MAUSB_MAUSB_DRIVER_STATUS_H__ */
diff --git a/drivers/usb/mausb_host/utils.c b/drivers/usb/mausb_host/utils.c
index c055b578e402..643671821709 100644
--- a/drivers/usb/mausb_host/utils.c
+++ b/drivers/usb/mausb_host/utils.c
@@ -13,6 +13,8 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 
+#include "mausb_driver_status.h"
+
 #define MAUSB_KERNEL_DEV_NAME "mausb_host"
 #define MAUSB_READ_DEVICE_TIMEOUT_MS 500
 
@@ -20,6 +22,47 @@ static dev_t mausb_major_kernel;
 static struct cdev  mausb_kernel_dev;
 static struct class *mausb_kernel_class;
 
+static void mausb_vm_open(struct vm_area_struct *vma)
+{
+	mausb_pr_debug("");
+}
+
+static void mausb_vm_close(struct vm_area_struct *vma)
+{
+	struct mausb_ring_buffer *buffer = NULL, *next = NULL;
+	unsigned long flags = 0;
+	u64 ring_buffer_id = *(u64 *)(vma->vm_private_data);
+
+	mausb_pr_info("Releasing ring buffer with id: %llu", ring_buffer_id);
+	spin_lock_irqsave(&mss.lock, flags);
+	list_for_each_entry_safe(buffer, next, &mss.available_ring_buffers,
+				 list_entry) {
+		if (buffer->id == ring_buffer_id) {
+			list_del(&buffer->list_entry);
+			mausb_ring_buffer_destroy(buffer);
+			kfree(buffer);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+/*
+ * mausb_vm_fault is called the first time a memory area is accessed which is
+ * not in memory
+ */
+static vm_fault_t mausb_vm_fault(struct vm_fault *vmf)
+{
+	mausb_pr_debug("");
+	return 0;
+}
+
+static const struct vm_operations_struct mausb_vm_ops = {
+	.open  = mausb_vm_open,
+	.close = mausb_vm_close,
+	.fault = mausb_vm_fault,
+};
+
 static int mausb_file_open(struct inode *inode, struct file *filp)
 {
 	filp->private_data = NULL;
@@ -35,9 +78,215 @@ static int mausb_file_close(struct inode *inode, struct file *filp)
 	return 0;
 }
 
+static ssize_t mausb_file_read(struct file *filp, char __user *user_buffer,
+			       size_t size, loff_t *offset)
+{
+	ssize_t num_of_bytes_to_read = MAUSB_MAX_NUM_OF_MA_DEVS *
+				       sizeof(struct mausb_events_notification);
+	unsigned long num_of_bytes_not_copied;
+	int completed_events;
+	int ring_events;
+	struct mausb_ring_buffer *ring_buffer;
+	struct mausb_device	 *dev;
+	struct completion	 *ring_has_events;
+	u8 current_device = 0;
+	s8 fail_ret_val;
+	unsigned long flags;
+	unsigned long timeout;
+	long status;
+
+	/* Reset heartbeat timer events */
+	mausb_reset_heartbeat_cnt();
+
+	if ((ssize_t)size != num_of_bytes_to_read) {
+		mausb_pr_alert("Different expected bytes to read (%ld) from actual size (%ld)",
+			       num_of_bytes_to_read, size);
+		fail_ret_val = MAUSB_DRIVER_BAD_READ_BUFFER_SIZE;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	/* If suspend/hibernate happened delete all devices */
+	if (atomic_xchg(&mss.num_of_transitions_to_sleep, 0)) {
+		mausb_pr_alert("Suspend system event detected");
+		fail_ret_val = MAUSB_DRIVER_SYSTEM_SUSPENDED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	ring_has_events = &mss.rings_events.mausb_ring_has_events;
+	timeout = msecs_to_jiffies(MAUSB_READ_DEVICE_TIMEOUT_MS);
+	status = wait_for_completion_interruptible_timeout(ring_has_events,
+							   timeout);
+	reinit_completion(ring_has_events);
+
+	if (atomic_read(&mss.rings_events.mausb_stop_reading_ring_events)) {
+		mausb_pr_alert("Ring events stopped");
+		fail_ret_val = MAUSB_DRIVER_RING_EVENTS_STOPPED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	/* There are no new events - waiting for events hit timeout */
+	if (status == 0)
+		return MAUSB_DRIVER_READ_TIMEOUT;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mss.events[current_device].madev_addr = dev->madev_addr;
+		ring_buffer = dev->ring_buffer;
+		ring_events = atomic_xchg(&ring_buffer->mausb_ring_events, 0);
+		completed_events =
+			atomic_xchg(&ring_buffer->mausb_completed_user_events,
+				    0);
+		mss.events[current_device].num_of_events = (u16)ring_events;
+		mss.events[current_device].num_of_completed_events =
+				(u16)completed_events;
+		if (++current_device == MAUSB_MAX_NUM_OF_MA_DEVS)
+			break;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	num_of_bytes_to_read =
+		(ssize_t)(current_device *
+			  sizeof(struct mausb_events_notification));
+	num_of_bytes_not_copied =
+		copy_to_user(user_buffer, &mss.events,
+			     (unsigned long)num_of_bytes_to_read);
+
+	mausb_pr_debug("num_of_bytes_not_copied %ld, num_of_bytes_to_read %ld",
+		       num_of_bytes_not_copied, num_of_bytes_to_read);
+
+	if (num_of_bytes_not_copied) {
+		fail_ret_val = MAUSB_DRIVER_COPY_TO_BUFFER_FAILED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	return num_of_bytes_to_read;
+}
+
+static ssize_t mausb_file_write(struct file *filp, const char __user *buffer,
+				size_t size, loff_t *offset)
+{
+	ssize_t num_of_bytes_to_write =
+				sizeof(struct mausb_events_notification);
+	struct mausb_events_notification notification;
+	unsigned long flags;
+	struct mausb_device *dev;
+
+	if (size != (size_t)num_of_bytes_to_write) {
+		mausb_pr_alert("Different expected bytes to write (%ld) from actual size (%ld)",
+			       num_of_bytes_to_write, size);
+		return MAUSB_DRIVER_WRITE_ERROR;
+	}
+
+	if (copy_from_user(&notification, buffer, size))
+		return MAUSB_DRIVER_WRITE_ERROR;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(notification.madev_addr);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return 0;
+	}
+
+	spin_lock_irqsave(&dev->num_of_user_events_lock, flags);
+	dev->num_of_user_events += notification.num_of_events;
+	dev->num_of_completed_events += notification.num_of_completed_events;
+	spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags);
+
+	queue_work(dev->workq, &dev->work);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return num_of_bytes_to_write;
+}
+
+static inline unsigned long mausb_ring_buffer_length(void)
+{
+	int page_order = mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+					      sizeof(struct mausb_event));
+	return PAGE_SIZE << page_order;
+}
+
+static int mausb_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	unsigned long size = vma->vm_end - vma->vm_start;
+	int ret;
+	struct page *page = NULL;
+	unsigned long flags = 0;
+	struct mausb_ring_buffer *ring_buffer = kzalloc(sizeof(*ring_buffer),
+							GFP_KERNEL);
+	if (!ring_buffer)
+		return -ENOMEM;
+
+	ret = mausb_ring_buffer_init(ring_buffer);
+	if (ret < 0) {
+		mausb_pr_err("Ring buffer init failed");
+		goto release_ring_buffer;
+	}
+
+	vma->vm_private_data = kzalloc(sizeof(ring_buffer->id), GFP_KERNEL);
+	if (!vma->vm_private_data) {
+		ret = -ENOMEM;
+		goto release_ring_buffer;
+	}
+
+	filp->private_data = vma->vm_private_data;
+
+	if (size > mausb_ring_buffer_length()) {
+		mausb_pr_err("Invalid memory size to map");
+		ret = -EINVAL;
+		goto release_ring_buffer;
+	}
+
+	vma->vm_ops = &mausb_vm_ops;
+	mausb_vm_open(vma);
+
+	page = virt_to_page(ring_buffer->to_user_buffer);
+	ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size,
+			      vma->vm_page_prot);
+	if (ret < 0) {
+		mausb_pr_err("Could not map the address area");
+		goto release_ring_buffer;
+	}
+
+	spin_lock_irqsave(&mss.lock, flags);
+	ring_buffer->id = mss.ring_buffer_id++;
+	*(u64 *)(vma->vm_private_data) = ring_buffer->id;
+	list_add_tail(&ring_buffer->list_entry, &mss.available_ring_buffers);
+	mausb_pr_info("Allocated ring buffer with id: %llu", ring_buffer->id);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+
+release_ring_buffer:
+	mausb_ring_buffer_destroy(ring_buffer);
+	kfree(ring_buffer);
+	return ret;
+}
+
 static const struct file_operations mausb_file_ops = {
 	.open	 = mausb_file_open,
 	.release = mausb_file_close,
+	.read	 = mausb_file_read,
+	.write   = mausb_file_write,
+	.mmap	 = mausb_mmap,
 };
 
 int mausb_create_dev(void)
@@ -83,3 +332,29 @@ void mausb_cleanup_dev(int device_created)
 
 	unregister_chrdev_region(mausb_major_kernel, 1);
 }
+
+void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer)
+{
+	int completed;
+
+	completed =
+		atomic_inc_return(&ring_buffer->mausb_completed_user_events);
+	mausb_pr_debug("mausb_completed_user_events INCREMENTED %d", completed);
+	if (completed > MAUSB_RING_BUFFER_SIZE / 16)
+		complete(&mss.rings_events.mausb_ring_has_events);
+}
+
+void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer)
+{
+	int events;
+
+	events = atomic_inc_return(&ring_buffer->mausb_ring_events);
+	if (events == 1)
+		complete(&mss.rings_events.mausb_ring_has_events);
+}
+
+void mausb_stop_ring_events(void)
+{
+	atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 1);
+	complete(&mss.rings_events.mausb_ring_has_events);
+}
diff --git a/drivers/usb/mausb_host/utils.h b/drivers/usb/mausb_host/utils.h
index 699f94fcb75b..e3ddb12afadd 100644
--- a/drivers/usb/mausb_host/utils.h
+++ b/drivers/usb/mausb_host/utils.h
@@ -5,6 +5,8 @@
 #ifndef __MAUSB_UTILS_H__
 #define __MAUSB_UTILS_H__
 
+#include "hpal.h"
+
 #if defined(MAUSB_NO_LOGS)
 #define mausb_pr_logs(...)
 #else
@@ -36,5 +38,8 @@
 
 int mausb_create_dev(void);
 void mausb_cleanup_dev(int device_created);
+void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer);
+void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer);
+void mausb_stop_ring_events(void);
 
 #endif /* __MAUSB_UTILS_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v4 8/8] usb: mausb_host: Process MA-USB data packets
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
                     ` (6 preceding siblings ...)
  2020-03-27 15:26   ` [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
@ 2020-03-27 15:26   ` vladimir.stankovic
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
  8 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-03-27 15:26 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Added implementation of MA-USB data&isoch packets processing logic,
both for IN and OUT directions.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile    |   1 +
 drivers/usb/mausb_host/hpal.c      |  32 +-
 drivers/usb/mausb_host/hpal_data.c | 719 +++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal_data.h |  34 ++
 4 files changed, 784 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal_data.c
 create mode 100644 drivers/usb/mausb_host/hpal_data.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index fd2a36a04ad6..a5fd033c002e 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -12,5 +12,6 @@ mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
 mausb_host-y += hpal_events.o
+mausb_host-y += hpal_data.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index 1e7bbe3b230a..ecbb8e6d6f84 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -11,6 +11,7 @@
 #include <linux/uio.h>
 
 #include "hcd.h"
+#include "hpal_data.h"
 #include "hpal_events.h"
 #include "utils.h"
 
@@ -1387,6 +1388,7 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
 int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 {
 	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
 
 	if (event->status != 0) {
 		mausb_pr_err("Event %d failed with status %d",
@@ -1401,9 +1403,22 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled for event=%d",
 			      event->type);
+		return status;
 	}
 
-	return 0;
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_send_isoch_in_msg(dev, event);
+		else
+			status = mausb_send_isoch_out_msg(dev, event, urb_ctx);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_send_in_data_msg(dev, event);
+		else
+			status = mausb_send_out_data_msg(dev, event, urb_ctx);
+	}
+
+	return status;
 }
 
 int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
@@ -1426,6 +1441,20 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 	if (!urb_ctx) {
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled");
+		goto cleanup;
+	}
+
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_receive_isoch_in_data(dev, event,
+							     urb_ctx);
+		else
+			status = mausb_receive_isoch_out(event);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			mausb_receive_in_data(event, urb_ctx);
+		else
+			mausb_receive_out_data(event, urb_ctx);
 	}
 
 cleanup:
@@ -1593,7 +1622,6 @@ static void mausb_handle_receive_event(struct mausb_device *dev,
 	status = mausb_msg_received_event(&event,
 					  (struct ma_usb_hdr_common *)data,
 					  channel);
-
 	if (status == 0)
 		status = mausb_enqueue_event_to_user(dev, &event);
 
diff --git a/drivers/usb/mausb_host/hpal_data.c b/drivers/usb/mausb_host/hpal_data.c
new file mode 100644
index 000000000000..bf076418e596
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_data.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_data.h"
+
+#include <linux/slab.h>
+#include <linux/uio.h>
+
+#include "hcd.h"
+#include "hpal.h"
+#include "hpal_events.h"
+#include "utils.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[2];
+	struct urb *urb   = (struct urb *)(event->data.urb);
+	bool setup_packet = (usb_endpoint_xfer_control(&urb->ep->desc) &&
+			     urb->setup_packet);
+	u32 kvec_num = setup_packet ? 2 : 1;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= kvec_num;
+	data_to_send.length	= MAUSB_TRANSFER_HDR_SIZE +
+			(setup_packet ? MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base = event->data.hdr;
+	kvec[0].iov_len  = MAUSB_TRANSFER_HDR_SIZE;
+
+	/* Prepare setup packet kvec */
+	if (setup_packet) {
+		kvec[1].iov_base = urb->setup_packet;
+		kvec[1].iov_len  = MAUSB_CONTROL_SETUP_SIZE;
+	}
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+	struct mausb_data_iter *iterator     = &urb_ctx->iterator;
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	void *buffer;
+	u32 payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE;
+	u32 data_written = 0;
+
+	buffer = shift_ptr(common_hdr, MAUSB_TRANSFER_HDR_SIZE);
+	data_written = mausb_data_iterator_write(iterator, buffer,
+						 payload_size);
+
+	mausb_pr_debug("data_written=%d, payload_size=%d", data_written,
+		       payload_size);
+	event->data.rem_transfer_size -= data_written;
+
+	if (event->data.transfer_eot) {
+		mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+			       event->data.transfer_size,
+			       event->data.rem_transfer_size, event->status);
+		mausb_complete_request(urb, event->data.transfer_size -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static int
+mausb_init_data_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				 struct list_head *chunks_list,
+				 u32 *num_of_data_chunks)
+{
+	int status = mausb_add_data_chunk(common_hdr, MAUSB_TRANSFER_HDR_SIZE,
+					  chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int mausb_init_control_data_chunk(struct mausb_event *event,
+					 struct list_head *chunks_list,
+					 u32 *num_of_data_chunks)
+{
+	int status;
+	void *buffer = ((struct urb *)event->data.urb)->setup_packet;
+
+	if (!event->data.first_control_packet)
+		return 0;
+
+	status = mausb_add_data_chunk(buffer, MAUSB_CONTROL_SETUP_SIZE,
+				      chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int
+mausb_prepare_transfer_packet(struct mausb_kvec_data_wrapper *wrapper,
+			      struct mausb_event *event,
+			      struct mausb_data_iter *iterator)
+{
+	u32 num_of_data_chunks		= 0;
+	u32 num_of_payload_data_chunks	= 0;
+	u32 payload_data_size		= 0;
+	int status = 0;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	struct ma_usb_hdr_common *data_hdr = (struct ma_usb_hdr_common *)
+			event->data.hdr;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_data_out_header_chunk(data_hdr, &chunks_list,
+					     &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/*
+	 * Initialize data chunk for MAUSB control setup packet and
+	 * add it to chunks list
+	 */
+	if (mausb_init_control_data_chunk(event, &chunks_list,
+					  &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size =
+			((struct ma_usb_hdr_common *)event->data.hdr)->length -
+			 MAUSB_TRANSFER_HDR_SIZE -
+			 (event->data.first_control_packet ?
+			  MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	if (mausb_data_iterator_read(iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks: /* Cleanup all allocated data chunks */
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	int status;
+	struct mausb_kvec_data_wrapper data;
+	enum mausb_channel channel;
+
+	status = mausb_prepare_transfer_packet(&data, event,
+					       &urb_ctx->iterator);
+
+	if (status < 0) {
+		mausb_pr_err("Failed to prepare transfer packet");
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data);
+
+	kfree(data.kvec);
+
+	return status;
+}
+
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size, event->data.rem_transfer_size,
+		       event->status);
+
+	if (event->data.transfer_eot) {
+		mausb_complete_request(urb, urb->transfer_buffer_length -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static inline u32
+__mausb_isoch_prepare_read_size_block(struct ma_usb_hdr_isochreadsizeblock_std *
+				      isoch_readsize_block, struct urb *urb)
+{
+	u32 i;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+
+	if (number_of_packets == 0)
+		return 0;
+
+	isoch_readsize_block->service_intervals  = number_of_packets;
+	isoch_readsize_block->max_segment_length =
+					(u32)urb->iso_frame_desc[0].length;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+
+	return sizeof(struct ma_usb_hdr_isochreadsizeblock_std);
+}
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	u32 read_size_block_length = 0;
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[MAUSB_ISOCH_IN_KVEC_NUM];
+	struct ma_usb_hdr_isochtransfer_optional opt_isoch_hdr;
+	struct ma_usb_hdr_isochreadsizeblock_std isoch_readsize_block;
+	struct ma_usb_hdr_common *hdr =
+				(struct ma_usb_hdr_common *)event->data.hdr;
+	struct urb *urb = (struct urb *)event->data.urb;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= 0;
+	data_to_send.length	= 0;
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base     = event->data.hdr;
+	kvec[0].iov_len	     = MAUSB_TRANSFER_HDR_SIZE;
+	data_to_send.length += (u32)kvec[0].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare optional header kvec */
+	opt_isoch_hdr.timestamp = MA_USB_TRANSFER_RESERVED;
+	opt_isoch_hdr.mtd	= MA_USB_TRANSFER_RESERVED;
+
+	kvec[1].iov_base     = &opt_isoch_hdr;
+	kvec[1].iov_len	     = sizeof(struct ma_usb_hdr_isochtransfer_optional);
+	data_to_send.length += (u32)kvec[1].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare read size blocks */
+	read_size_block_length =
+		__mausb_isoch_prepare_read_size_block(&isoch_readsize_block,
+						      urb);
+	if (read_size_block_length > 0) {
+		kvec[2].iov_base     = &isoch_readsize_block;
+		kvec[2].iov_len	     = read_size_block_length;
+		data_to_send.length += (u32)kvec[2].iov_len;
+		data_to_send.kvec_num++;
+	}
+
+	hdr->length = (u16)data_to_send.length;
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+static void __mausb_process_in_isoch_short_resp(struct mausb_event *event,
+						struct ma_usb_hdr_common *hdr,
+						struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_short *data_block_hdr =
+			(struct ma_usb_hdr_isochdatablock_short *)
+			shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+				  opt_hdr_shift);
+	u8 *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers *
+				   sizeof(*data_block_hdr));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = urb_ctx->urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			     event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num  = data_block_hdr[i].segment_number;
+		u16 seg_size = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, urb.number_of_packets=%d",
+				     event->data.ep_handle, seg_num,
+				     urb->number_of_packets);
+			break;
+		}
+
+		if (seg_size > urb->iso_frame_desc[seg_num].length) {
+			mausb_pr_err("Block to long for segment: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, seg_size) > end_of_packet) {
+			mausb_pr_err("End of segment after enf of packet: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset);
+		mausb_data_iterator_write(&urb_ctx->iterator, isoch_data,
+					  seg_size);
+
+		isoch_data = shift_ptr(isoch_data, seg_size);
+
+		urb->iso_frame_desc[seg_num].actual_length = seg_size;
+		urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+static void __mausb_process_in_isoch_std_resp(struct mausb_event *event,
+					      struct ma_usb_hdr_common *hdr,
+					      struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_std *data_block_hdr =
+		(struct ma_usb_hdr_isochdatablock_std *)
+		shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+			  opt_hdr_shift);
+	u8 *isoch_data =
+		shift_ptr(data_block_hdr, hdr->data.headers *
+			  sizeof(struct ma_usb_hdr_isochdatablock_std));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = (struct urb *)event->data.urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			     event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num   = data_block_hdr[i].segment_number;
+		u16 seg_len   = data_block_hdr[i].segment_length;
+		u16 block_len = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, number_of_packets=%d",
+				     event->data.ep_handle, seg_num,
+				     urb->number_of_packets);
+			break;
+		}
+
+		if (block_len > urb->iso_frame_desc[seg_num].length -
+			     urb->iso_frame_desc[seg_num].actual_length) {
+			mausb_pr_err("Block too long for segment: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, block_len) >
+				       end_of_packet) {
+			mausb_pr_err("End of fragment after end of packet: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset +
+					 data_block_hdr[i].fragment_offset);
+		mausb_data_iterator_write(&urb_ctx->iterator,
+					  isoch_data, block_len);
+		isoch_data = shift_ptr(isoch_data, block_len);
+
+		urb->iso_frame_desc[seg_num].actual_length += block_len;
+
+		if (urb->iso_frame_desc[seg_num].actual_length == seg_len)
+			urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+int mausb_receive_isoch_in_data(struct mausb_device *dev,
+				struct mausb_event *event,
+				struct mausb_urb_ctx *urb_ctx)
+{
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	struct ma_usb_hdr_transfer *transfer_hdr =
+					mausb_get_data_transfer_hdr(common_hdr);
+
+	if (!(common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK)) {
+		/* Short ISO headers response */
+		__mausb_process_in_isoch_short_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_STD) {
+		/* Standard ISO headers response */
+		__mausb_process_in_isoch_std_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_LONG) {
+		/* Long ISO headers response */
+		mausb_pr_warn("Long isoc headers in response: ep_handle=%#x, req_id=%#x",
+			      event->data.ep_handle, transfer_hdr->req_id);
+	} else {
+		/* Error */
+		mausb_pr_err("Isoc header error in response: ep_handle=%#x, req_id=%#x",
+			     event->data.ep_handle, transfer_hdr->req_id);
+	}
+
+	return 0;
+}
+
+static inline u32
+__mausb_calculate_isoch_common_header_size(u32 num_of_segments)
+{
+	return MAUSB_ISOCH_TRANSFER_HDR_SIZE +
+			MAUSB_ISOCH_STANDARD_FORMAT_SIZE * num_of_segments;
+}
+
+static struct ma_usb_hdr_common *
+__mausb_create_isoch_out_transfer_packet(struct mausb_event *event,
+					 struct mausb_urb_ctx *urb_ctx,
+					 u16 payload_size, u32 seq_n,
+					 u32 start_of_segments,
+					 u32 number_of_segments)
+{
+	struct ma_usb_hdr_common		 *hdr;
+	struct ma_usb_hdr_isochtransfer		 *hdr_isochtransfer;
+	struct ma_usb_hdr_isochdatablock_std	 *isoc_header_std;
+	struct ma_usb_hdr_isochtransfer_optional *hdr_opt_isochtransfer;
+	struct urb *urb = (struct urb *)event->data.urb;
+	void *isoc_headers = NULL;
+	u32 length;
+	u16 i;
+	unsigned long block_length;
+	u32 number_of_packets = (u32)event->data.isoch_seg_num;
+	u32 size_of_request =
+		__mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr = kzalloc(size_of_request, GFP_KERNEL);
+	if (!hdr)
+		return NULL;
+
+	hdr->version	  = MA_USB_HDR_VERSION_1_0;
+	hdr->ssid	  = event->data.mausb_ssid;
+	hdr->flags	  = MA_USB_HDR_FLAGS_HOST;
+	hdr->dev_addr	  = event->data.mausb_address;
+	hdr->handle.epv	  = event->data.ep_handle;
+	hdr->data.status  = MA_USB_HDR_STATUS_NO_ERROR;
+	hdr->data.eps	  = MAUSB_TRANSFER_RESERVED;
+	hdr->data.t_flags = (u8)(usb_endpoint_type(&urb->ep->desc) << 3);
+
+	isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE);
+
+	for (i = (u16)start_of_segments;
+	     i < number_of_segments + start_of_segments; ++i) {
+		block_length = i < number_of_packets - 1 ?
+			urb->iso_frame_desc[i + 1].offset -
+			urb->iso_frame_desc[i].offset :
+			mausb_data_iterator_length(&urb_ctx->iterator) -
+			urb->iso_frame_desc[i].offset;
+
+		urb->iso_frame_desc[i].status = MA_USB_HDR_STATUS_UNSUCCESSFUL;
+		isoc_header_std = (struct ma_usb_hdr_isochdatablock_std *)
+			shift_ptr(isoc_headers,
+				  (u64)MAUSB_ISOCH_STANDARD_FORMAT_SIZE *
+				  (i - start_of_segments));
+		isoc_header_std->block_length	 = (u16)block_length;
+		isoc_header_std->segment_number  = i;
+		isoc_header_std->s_flags	 = 0;
+		isoc_header_std->segment_length  = (u16)block_length;
+		isoc_header_std->fragment_offset = 0;
+	}
+
+	length = __mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr->flags		|= MA_USB_HDR_FLAGS_TIMESTAMP;
+	hdr->type		 = (u8)MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER);
+	hdr->data.headers	 = (u16)number_of_segments;
+	hdr->data.i_flags	 = MA_USB_DATA_IFLAGS_HDR_FMT_STD |
+				      MA_USB_DATA_IFLAGS_ASAP;
+	hdr_opt_isochtransfer	    = mausb_hdr_isochtransfer_optional_hdr(hdr);
+	hdr_isochtransfer	    = mausb_get_isochtransfer_hdr(hdr);
+	hdr_isochtransfer->req_id   = event->data.req_id;
+	hdr_isochtransfer->seq_n    = seq_n;
+	hdr_isochtransfer->segments = number_of_packets;
+
+	hdr_isochtransfer->presentation_time = MA_USB_TRANSFER_RESERVED;
+
+	hdr_opt_isochtransfer->timestamp = MA_USB_TRANSFER_RESERVED;
+	hdr_opt_isochtransfer->mtd	 = MA_USB_TRANSFER_RESERVED;
+
+	hdr->length = (u16)length + payload_size;
+
+	return hdr;
+}
+
+static int
+mausb_init_isoch_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				  struct list_head *chunks_list,
+				  u32 *num_of_data_chunks,
+				  u32 num_of_packets)
+{
+	u32 header_size =
+		__mausb_calculate_isoch_common_header_size(num_of_packets);
+	int status = mausb_add_data_chunk(common_hdr, header_size, chunks_list);
+
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static
+int mausb_prepare_isoch_out_transfer_packet(struct ma_usb_hdr_common *hdr,
+					    struct mausb_event *event,
+					    struct mausb_urb_ctx *urb_ctx,
+					    struct mausb_kvec_data_wrapper *
+					    result_data_wrapper)
+{
+	u32 num_of_data_chunks	       = 0;
+	u32 num_of_payload_data_chunks = 0;
+	u32 segment_number	       = event->data.isoch_seg_num;
+	u32 payload_data_size;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	int status = 0;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_isoch_out_header_chunk(hdr, &chunks_list,
+					      &num_of_data_chunks,
+					      segment_number) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size = hdr->length -
+		__mausb_calculate_isoch_common_header_size(segment_number);
+
+	if (mausb_data_iterator_read(&urb_ctx->iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		mausb_pr_err("Data iterator read failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(result_data_wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		mausb_pr_err("Data wrapper init failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks:
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+static int mausb_create_and_send_isoch_transfer_req(struct mausb_device *dev,
+						    struct mausb_event *event,
+						    struct mausb_urb_ctx
+						    *urb_ctx, u32 *seq_n,
+						    u32 payload_size,
+						    u32 start_of_segments,
+						    u32 number_of_segments)
+{
+	struct ma_usb_hdr_common *hdr;
+	struct mausb_kvec_data_wrapper data_to_send;
+	int status;
+	enum mausb_channel channel;
+
+	hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx,
+						       (u16)payload_size,
+						       *seq_n,
+						       start_of_segments,
+						       number_of_segments);
+	if (!hdr) {
+		mausb_pr_alert("Isoch transfer packet alloc failed");
+		return -ENOMEM;
+	}
+	*seq_n = (*seq_n + 1) % (MA_USB_TRANSFER_SEQN_MAX + 1);
+
+	status = mausb_prepare_isoch_out_transfer_packet(hdr, event, urb_ctx,
+							 &data_to_send);
+	if (status < 0) {
+		mausb_pr_alert("Failed to prepare transfer packet");
+		kfree(hdr);
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data_to_send);
+
+	kfree(hdr);
+	kfree(data_to_send.kvec);
+
+	return status;
+}
+
+static inline int __mausb_send_isoch_out_packet(struct mausb_device *dev,
+						struct mausb_event *event,
+						struct mausb_urb_ctx *urb_ctx,
+						u32 *seq_n,
+						u32 *starting_segments,
+						u32 *rem_transfer_buf,
+						u32 *payload_size, u32 index)
+{
+	int status = mausb_create_and_send_isoch_transfer_req(dev, event,
+					urb_ctx, seq_n, *payload_size,
+					*starting_segments,
+					index - *starting_segments);
+	if (status < 0) {
+		mausb_pr_err("ISOCH transfer request create and send failed");
+		return status;
+	}
+	*starting_segments = index;
+	*rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	*payload_size	   = 0;
+
+	return 0;
+}
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx)
+{
+	u32   starting_segments = 0;
+	u32   rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	struct urb *urb = (struct urb *)mausb_event->data.urb;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+	u32 payload_size   = 0;
+	u32 chunk_size;
+	u32 seq_n	   = 0;
+	int status;
+	u32 i;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		if (i < number_of_packets - 1)
+			chunk_size = urb->iso_frame_desc[i + 1].offset -
+					urb->iso_frame_desc[i].offset;
+		else
+			chunk_size =
+				mausb_data_iterator_length(&urb_ctx->iterator) -
+						urb->iso_frame_desc[i].offset;
+
+		if (chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE >
+		    rem_transfer_buf) {
+			if (payload_size == 0) {
+				mausb_pr_warn("Fragmentation");
+			} else {
+				status = __mausb_send_isoch_out_packet
+						(ma_dev, mausb_event, urb_ctx,
+						 &seq_n, &starting_segments,
+						 &rem_transfer_buf,
+						 &payload_size, i);
+				if (status < 0)
+					return status;
+				i--;
+				continue;
+			}
+		} else {
+			rem_transfer_buf -=
+				chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE;
+			payload_size += chunk_size;
+		}
+
+		if (i == number_of_packets - 1 || rem_transfer_buf == 0) {
+			status = __mausb_send_isoch_out_packet
+					(ma_dev, mausb_event, urb_ctx, &seq_n,
+					 &starting_segments, &rem_transfer_buf,
+					 &payload_size, i + 1);
+			if (status < 0)
+				return status;
+		}
+	}
+	return 0;
+}
+
+int mausb_receive_isoch_out(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	int status = 0;
+	u16 i;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size, event->data.rem_transfer_size,
+		       event->status);
+
+	for (i = 0; i < urb->number_of_packets; ++i)
+		urb->iso_frame_desc[i].status = event->status;
+
+	mausb_complete_request(urb, event->data.payload_size, event->status);
+
+	return status;
+}
diff --git a/drivers/usb/mausb_host/hpal_data.h b/drivers/usb/mausb_host/hpal_data.h
new file mode 100644
index 000000000000..8d9650e5fb75
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_data.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_DATA_H__
+#define __MAUSB_HPAL_DATA_H__
+
+#include <linux/types.h>
+
+#include "hpal_events.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event);
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+
+#define MAUSB_ISOCH_IN_KVEC_NUM 3
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev,
+			    struct mausb_event *event);
+int mausb_receive_isoch_in_data(struct mausb_device *dev,
+				struct mausb_event *event,
+				struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx);
+int mausb_receive_isoch_out(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_DATA_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 1/8] usb: Add MA-USB Host kernel module
  2020-03-27 15:26   ` [PATCH v4 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
@ 2020-03-27 16:25     ` Alan Stern
  0 siblings, 0 replies; 62+ messages in thread
From: Alan Stern @ 2020-03-27 16:25 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Fri, 27 Mar 2020 vladimir.stankovic@displaylink.com wrote:

> Added utility macros, kernel device creation and cleanup, functions for
> handling log formatting and a placeholder module for MA-USB Host device
> driver.
> 
> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>

> index 235ab38ed478..12aac44196d7 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10226,6 +10226,13 @@ W:	https://linuxtv.org
>  S:	Maintained
>  F:	drivers/media/radio/radio-maxiradio*
>  
> +MA USB HOST DRIVER

It would be an excellent idea to change this title to:

MA (MEDIA-AGNOSTIC) USB HOST DRIVER

Otherwise to me it looks like the Massachusetts USB HOST DRIVER, since
MA is the 2-letter abbreviation for the state where I live.  A
two-letter acronym is just too generic; you mustn't expect people to
understand it.

> +M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> +L:	mausb-host-devel@displaylink.com
> +W:	https://www.displaylink.com
> +S:	Maintained
> +F:	drivers/usb/mausb_host/*
> +

> --- /dev/null
> +++ b/drivers/usb/mausb_host/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Kernel configuration file for MA-USB Host driver.
> +#
> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> +#
> +
> +config HOST_MAUSB
> +	tristate "MA-USB Host Driver"
> +	depends on USB=y
> +	default n

No need for this line; n is always the default setting unless you
specify otherwise.

> +	help
> +		This is a MA-USB Host driver which enables host communication
> +		via MA-USB protocol stack.

And I insist that you insert "Media Agnostic" in here somewhere, either 
in the "tristate" line or in the help.  Without this, people trying to 
configure their kernels will have no idea whether or not they should 
enable the setting because they won't know what MA-USB is.

It also wouldn't hurt to mention in the help text that if the driver is
built as a module, the name of the module will be "mausb_host".

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing
  2020-03-27 15:26   ` [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
@ 2020-03-27 16:35     ` Alan Stern
  2020-03-28  3:56     ` kbuild test robot
  1 sibling, 0 replies; 62+ messages in thread
From: Alan Stern @ 2020-03-27 16:35 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Fri, 27 Mar 2020 vladimir.stankovic@displaylink.com wrote:

> Protocol adaptation layer (PAL) implementation has been added to
> introduce MA-USB structures and logic.
> 
> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> ---
> +	/*
> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
> +	 * should not, so it is breaking the USB drive on the linux
> +	 */
> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;

Please explain more fully.  What SCSI driver are you talking about?  
Where is the flag getting added incorrectly?  What USB drive is getting
broken, and how?

If there's a bug in a SCSI driver, why don't you report it and get it
fixed instead of trying to create a workaround in just one host 
controller driver?

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers
  2020-03-27 15:26   ` [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
@ 2020-03-27 16:37     ` Alan Stern
  2020-04-13 15:16       ` Vladimir Stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Alan Stern @ 2020-03-27 16:37 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Fri, 27 Mar 2020 vladimir.stankovic@displaylink.com wrote:

> Implemented handlers for subset of HCD events.
> 
> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> ---
> +/*
> + * For usb 2.0 logitech camera called multiple times, once during
> + * enumeration of device and later after mausb_reset_device.
> + */
> +static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)

> +/*
> + * For usb 2.0 logitech camera called multiple times, once during enumeration
> + * of device and later after mausb_reset_device. In latter case it is
> + * required to address the device again in order for ep0 to work properly.
> + */
> +static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)

> +/*
> + * For usb 2.0 logitech camera called multiple times,
> + * followed by either mausb_enable_device or mausb_address_device.
> + * Resets device to non-addressed state.
> + */
> +static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)

Why on earth do you have all these comments about Logitech cameras?  A 
host controller driver shouldn't care about the type of devices it gets 
connected to.

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing
  2020-03-27 15:26   ` [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
  2020-03-27 16:35     ` Alan Stern
@ 2020-03-28  3:56     ` kbuild test robot
  1 sibling, 0 replies; 62+ messages in thread
From: kbuild test robot @ 2020-03-28  3:56 UTC (permalink / raw)
  To: vladimir.stankovic
  Cc: kbuild-all, gregkh, linux-kernel, linux-usb, mausb-host-devel

[-- Attachment #1: Type: text/plain, Size: 9599 bytes --]

Hi,

I love your patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on balbi-usb/next peter.chen-usb/ci-for-usb-next linus/master linux/master v5.6-rc7 next-20200327]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/vladimir-stankovic-displaylink-com/Add-MA-USB-Host-driver/20200328-042436
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: c6x-allyesconfig (attached as .config)
compiler: c6x-elf-gcc (GCC) 9.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=9.2.0 make.cross ARCH=c6x 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/usb/mausb_host/hpal.c: In function 'mausb_release_event_resources':
>> drivers/usb/mausb_host/hpal.c:221:45: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     221 |  struct ma_usb_hdr_common *receive_buffer = (struct ma_usb_hdr_common *)
         |                                             ^
   drivers/usb/mausb_host/hpal.c: In function 'mausb_complete_urb':
   drivers/usb/mausb_host/hpal.c:229:20: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     229 |  struct urb *urb = (struct urb *)event->data.urb;
         |                    ^
   drivers/usb/mausb_host/hpal.c: In function 'mausb_data_req_enqueue_event':
>> drivers/usb/mausb_host/hpal.c:357:26: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     357 |  mausb_event.data.urb  = (u64)request;
         |                          ^
   drivers/usb/mausb_host/hpal.c: In function 'mausb_execute_urb_dequeue':
   drivers/usb/mausb_host/hpal.c:492:32: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     492 |  mausb_event.data.urb        = (u64)urb;
         |                                ^
   drivers/usb/mausb_host/hpal.c: In function 'mausb_send_data_msg':
   drivers/usb/mausb_host/hpal.c:725:35: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     725 |  urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
         |                                   ^
   drivers/usb/mausb_host/hpal.c: In function 'mausb_receive_data_msg':
   drivers/usb/mausb_host/hpal.c:752:35: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     752 |  urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
         |                                   ^
   /tmp/ccABqgDp.s: Assembler messages:
   /tmp/ccABqgDp.s:3838: Warning: ignoring changed section attributes for .far

vim +221 drivers/usb/mausb_host/hpal.c

   218	
   219	void mausb_release_event_resources(struct mausb_event *event)
   220	{
 > 221		struct ma_usb_hdr_common *receive_buffer = (struct ma_usb_hdr_common *)
   222							    event->data.recv_buf;
   223	
   224		kfree(receive_buffer);
   225	}
   226	
   227	void mausb_complete_urb(struct mausb_event *event)
   228	{
   229		struct urb *urb = (struct urb *)event->data.urb;
   230	
   231		mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
   232			       event->data.transfer_size,
   233			       event->data.rem_transfer_size, event->status);
   234		mausb_complete_request(urb,
   235				       event->data.transfer_size -
   236				       event->data.rem_transfer_size,
   237				       event->status);
   238	}
   239	
   240	static int mausb_get_first_free_port_number(u16 *port_number)
   241	{
   242		(*port_number) = 0;
   243		while ((mhcd->connected_ports & (1 << *port_number)) != 0 &&
   244		       *port_number < NUMBER_OF_PORTS)
   245			++(*port_number);
   246	
   247		if (*port_number == NUMBER_OF_PORTS)
   248			return -EINVAL;
   249	
   250		mhcd->connected_ports |= (1 << *port_number);
   251	
   252		return 0;
   253	}
   254	
   255	static inline void mausb_port_has_changed_event(struct mausb_device *dev,
   256							struct mausb_event *event)
   257	{
   258		int status;
   259		u16 port_number;
   260		unsigned long flags = 0;
   261	
   262		spin_lock_irqsave(&mhcd->lock, flags);
   263	
   264		status = mausb_get_first_free_port_number(&port_number);
   265		if (status < 0) {
   266			spin_unlock_irqrestore(&mhcd->lock, flags);
   267			mausb_pr_err("There is no free port, schedule delete ma_dev");
   268			queue_work(dev->workq, &dev->socket_disconnect_work);
   269			return;
   270		}
   271	
   272		spin_unlock_irqrestore(&mhcd->lock, flags);
   273	
   274		dev->dev_type	   = event->port_changed.dev_type;
   275		dev->dev_speed	   = event->port_changed.dev_speed;
   276		dev->lse	   = event->port_changed.lse;
   277		dev->dev_connected = 1;
   278		dev->port_number   = port_number;
   279	
   280		mausb_port_has_changed(event->port_changed.dev_type,
   281				       event->port_changed.dev_speed, dev);
   282	
   283		if ((enum mausb_device_type)event->port_changed.dev_type == USB30HUB)
   284			mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
   285	}
   286	
   287	static void mausb_heartbeat_timer_func(struct timer_list *timer)
   288	{
   289		unsigned long flags = 0;
   290		struct mausb_device *dev = NULL;
   291	
   292		if (mausb_start_heartbeat_timer() < 0) {
   293			mausb_pr_err("Devices disconnecting - app is unresponsive");
   294			spin_lock_irqsave(&mss.lock, flags);
   295	
   296			/* Reset connected clients */
   297			mss.client_connected = false;
   298			mss.missed_heartbeats = 0;
   299	
   300			list_for_each_entry(dev, &mss.madev_list, list_entry) {
   301				mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x",
   302					       dev->madev_addr);
   303				queue_work(dev->workq, &dev->heartbeat_work);
   304			}
   305	
   306			complete(&mss.client_stopped);
   307			spin_unlock_irqrestore(&mss.lock, flags);
   308		}
   309	}
   310	
   311	void mausb_release_ma_dev_async(struct kref *kref)
   312	{
   313		struct mausb_device *dev = container_of(kref, struct mausb_device,
   314							refcount);
   315	
   316		mausb_pr_info("Scheduling work for MAUSB device to be deleted");
   317	
   318		schedule_work(&dev->madev_delete_work);
   319	}
   320	
   321	int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
   322					  u16 num_of_completed)
   323	{
   324		unsigned long flags;
   325		struct mausb_device *dev;
   326	
   327		spin_lock_irqsave(&mss.lock, flags);
   328		dev = mausb_get_dev_from_addr_unsafe(madev_addr);
   329	
   330		if (!dev) {
   331			spin_unlock_irqrestore(&mss.lock, flags);
   332			return -EINVAL;
   333		}
   334	
   335		spin_lock(&dev->num_of_user_events_lock);
   336		dev->num_of_user_events += num_of_events;
   337		dev->num_of_completed_events += num_of_completed;
   338		spin_unlock(&dev->num_of_user_events_lock);
   339		queue_work(dev->workq, &dev->work);
   340		spin_unlock_irqrestore(&mss.lock, flags);
   341	
   342		return 0;
   343	}
   344	
   345	int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
   346					 struct urb *request)
   347	{
   348		struct mausb_event mausb_event;
   349	
   350		mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
   351		mausb_event.status = 0;
   352	
   353		mausb_event.data.transfer_type =
   354			mausb_transfer_type_from_usb(&request->ep->desc);
   355		mausb_event.data.device_id	= dev->id;
   356		mausb_event.data.ep_handle	= ep_handle;
 > 357		mausb_event.data.urb		= (u64)request;
   358		mausb_event.data.setup_packet	=
   359			(usb_endpoint_xfer_control(&request->ep->desc) &&
   360				request->setup_packet);
   361		mausb_event.data.transfer_size	= request->transfer_buffer_length;
   362		mausb_event.data.direction	= (usb_urb_dir_in(request) ?
   363							MAUSB_DATA_MSG_DIRECTION_IN :
   364							MAUSB_DATA_MSG_DIRECTION_OUT);
   365		mausb_event.data.transfer_size +=
   366			((mausb_event.data.direction == MAUSB_DATA_MSG_DIRECTION_OUT &&
   367				mausb_event.data.setup_packet) ?
   368					MAUSB_CONTROL_SETUP_SIZE : 0);
   369		mausb_event.data.rem_transfer_size = mausb_event.data.transfer_size;
   370		mausb_event.data.transfer_flags	   = request->transfer_flags;
   371		mausb_event.data.transfer_eot	   = false;
   372		mausb_event.data.isoch_seg_num	   = (u32)request->number_of_packets;
   373		mausb_event.data.recv_buf	   = 0;
   374		mausb_event.data.payload_size	   =
   375			(usb_endpoint_xfer_isoc(&request->ep->desc) &&
   376			 usb_endpoint_dir_out(&request->ep->desc)) ?
   377			(request->iso_frame_desc[request->number_of_packets - 1]
   378									.offset +
   379			 request->iso_frame_desc[request->number_of_packets - 1]
   380									.length) : 0;
   381	
   382		if (mausb_event.data.setup_packet) {
   383			memcpy(mausb_event.data.hdr_ack, request->setup_packet,
   384			       MAUSB_CONTROL_SETUP_SIZE);
   385			memcpy(shift_ptr(mausb_event.data.hdr_ack,
   386					 MAUSB_CONTROL_SETUP_SIZE),
   387			       &request->dev->route, sizeof(request->dev->route));
   388		}
   389	
   390		return 0;
   391	}
   392	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 51707 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing
  2020-03-27 15:26   ` [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
@ 2020-03-28 10:35     ` kbuild test robot
  2020-04-04 16:07     ` kbuild test robot
  1 sibling, 0 replies; 62+ messages in thread
From: kbuild test robot @ 2020-03-28 10:35 UTC (permalink / raw)
  To: vladimir.stankovic
  Cc: kbuild-all, gregkh, linux-kernel, linux-usb, mausb-host-devel

[-- Attachment #1: Type: text/plain, Size: 9163 bytes --]

Hi,

I love your patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on balbi-usb/next peter.chen-usb/ci-for-usb-next linus/master linux/master v5.6-rc7 next-20200327]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/vladimir-stankovic-displaylink-com/Add-MA-USB-Host-driver/20200328-042436
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: c6x-allyesconfig (attached as .config)
compiler: c6x-elf-gcc (GCC) 9.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=9.2.0 make.cross ARCH=c6x 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from include/linux/printk.h:7,
                    from include/linux/kernel.h:15,
                    from include/linux/list.h:9,
                    from include/linux/preempt.h:11,
                    from include/linux/spinlock.h:51,
                    from include/linux/kref.h:16,
                    from drivers/usb/mausb_host/hpal.h:8,
                    from drivers/usb/mausb_host/utils.h:8,
                    from drivers/usb/mausb_host/utils.c:5:
   drivers/usb/mausb_host/utils.c: In function 'mausb_file_read':
>> include/linux/kern_levels.h:5:18: warning: format '%ld' expects argument of type 'long int', but argument 5 has type 'ssize_t' {aka 'int'} [-Wformat=]
       5 | #define KERN_SOH "\001"  /* ASCII Start Of Header */
         |                  ^~~~~~
   include/linux/kern_levels.h:9:20: note: in expansion of macro 'KERN_SOH'
       9 | #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
         |                    ^~~~~~~~
   include/linux/printk.h:300:9: note: in expansion of macro 'KERN_ALERT'
     300 |  printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~
>> drivers/usb/mausb_host/utils.h:16:2: note: in expansion of macro 'pr_alert'
      16 |  pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
         |  ^~~
>> drivers/usb/mausb_host/utils.h:20:29: note: in expansion of macro 'mausb_pr_logs'
      20 | #define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
         |                             ^~~~~~~~~~~~~
>> drivers/usb/mausb_host/utils.c:102:3: note: in expansion of macro 'mausb_pr_alert'
     102 |   mausb_pr_alert("Different expected bytes to read (%ld) from actual size (%ld)",
         |   ^~~~~~~~~~~~~~
   include/linux/kern_levels.h:5:18: warning: format '%ld' expects argument of type 'long int', but argument 6 has type 'size_t' {aka 'unsigned int'} [-Wformat=]
       5 | #define KERN_SOH "\001"  /* ASCII Start Of Header */
         |                  ^~~~~~
   include/linux/kern_levels.h:9:20: note: in expansion of macro 'KERN_SOH'
       9 | #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
         |                    ^~~~~~~~
   include/linux/printk.h:300:9: note: in expansion of macro 'KERN_ALERT'
     300 |  printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~
>> drivers/usb/mausb_host/utils.h:16:2: note: in expansion of macro 'pr_alert'
      16 |  pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
         |  ^~~
>> drivers/usb/mausb_host/utils.h:20:29: note: in expansion of macro 'mausb_pr_logs'
      20 | #define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
         |                             ^~~~~~~~~~~~~
>> drivers/usb/mausb_host/utils.c:102:3: note: in expansion of macro 'mausb_pr_alert'
     102 |   mausb_pr_alert("Different expected bytes to read (%ld) from actual size (%ld)",
         |   ^~~~~~~~~~~~~~
   drivers/usb/mausb_host/utils.c: In function 'mausb_file_write':
>> include/linux/kern_levels.h:5:18: warning: format '%ld' expects argument of type 'long int', but argument 5 has type 'ssize_t' {aka 'int'} [-Wformat=]
       5 | #define KERN_SOH "\001"  /* ASCII Start Of Header */
         |                  ^~~~~~
   include/linux/kern_levels.h:9:20: note: in expansion of macro 'KERN_SOH'
       9 | #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
         |                    ^~~~~~~~
   include/linux/printk.h:300:9: note: in expansion of macro 'KERN_ALERT'
     300 |  printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~
>> drivers/usb/mausb_host/utils.h:16:2: note: in expansion of macro 'pr_alert'
      16 |  pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
         |  ^~~
>> drivers/usb/mausb_host/utils.h:20:29: note: in expansion of macro 'mausb_pr_logs'
      20 | #define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
         |                             ^~~~~~~~~~~~~
   drivers/usb/mausb_host/utils.c:193:3: note: in expansion of macro 'mausb_pr_alert'
     193 |   mausb_pr_alert("Different expected bytes to write (%ld) from actual size (%ld)",
         |   ^~~~~~~~~~~~~~
   include/linux/kern_levels.h:5:18: warning: format '%ld' expects argument of type 'long int', but argument 6 has type 'size_t' {aka 'unsigned int'} [-Wformat=]
       5 | #define KERN_SOH "\001"  /* ASCII Start Of Header */
         |                  ^~~~~~
   include/linux/kern_levels.h:9:20: note: in expansion of macro 'KERN_SOH'
       9 | #define KERN_ALERT KERN_SOH "1" /* action must be taken immediately */
         |                    ^~~~~~~~
   include/linux/printk.h:300:9: note: in expansion of macro 'KERN_ALERT'
     300 |  printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~
>> drivers/usb/mausb_host/utils.h:16:2: note: in expansion of macro 'pr_alert'
      16 |  pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
         |  ^~~
>> drivers/usb/mausb_host/utils.h:20:29: note: in expansion of macro 'mausb_pr_logs'
      20 | #define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
         |                             ^~~~~~~~~~~~~
   drivers/usb/mausb_host/utils.c:193:3: note: in expansion of macro 'mausb_pr_alert'
     193 |   mausb_pr_alert("Different expected bytes to write (%ld) from actual size (%ld)",
         |   ^~~~~~~~~~~~~~
--
   drivers/usb/mausb_host/hpal_events.c: In function 'mausb_data_msg_received_event':
>> drivers/usb/mausb_host/hpal_events.c:44:27: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
      44 |  event->data.recv_buf   = (u64)hdr;
         |                           ^
   drivers/usb/mausb_host/hpal_events.c: In function 'mausb_isoch_msg_received_event':
   drivers/usb/mausb_host/hpal_events.c:66:27: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
      66 |  event->data.recv_buf   = (u64)hdr;
         |                           ^
   drivers/usb/mausb_host/hpal_events.c: In function 'mausb_cleanup_delete_data_transfer_event':
>> drivers/usb/mausb_host/hpal_events.c:593:20: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     593 |  struct urb *urb = (struct urb *)event->data.urb;
         |                    ^

vim +/pr_alert +16 drivers/usb/mausb_host/utils.h

5f578d5631444d vladimir.stankovic@displaylink.com 2020-03-27   9  
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  10  #if defined(MAUSB_NO_LOGS)
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  11  #define mausb_pr_logs(...)
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  12  #else
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  13  #include <linux/printk.h>
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  14  #include <linux/sched.h>
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  15  #define mausb_pr_logs(level_str, level, log, ...)\
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27 @16  	pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  17  	current->pid, current->tgid, __func__, ##__VA_ARGS__)
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  18  #endif /* MAUSB_NO_LOGS */
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  19  
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27 @20  #define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
cca4e12f279232 vladimir.stankovic@displaylink.com 2020-03-27  21  

:::::: The code at line 16 was first introduced by commit
:::::: cca4e12f279232d69e2a551b72966976d5cda619 usb: Add MA-USB Host kernel module

:::::: TO: vladimir.stankovic@displaylink.com <vladimir.stankovic@displaylink.com>
:::::: CC: 0day robot <lkp@intel.com>

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 51707 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing
  2020-03-27 15:26   ` [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
  2020-03-28 10:35     ` kbuild test robot
@ 2020-04-04 16:07     ` kbuild test robot
  1 sibling, 0 replies; 62+ messages in thread
From: kbuild test robot @ 2020-04-04 16:07 UTC (permalink / raw)
  To: vladimir.stankovic
  Cc: kbuild-all, gregkh, linux-kernel, linux-usb, mausb-host-devel

Hi,

I love your patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on balbi-usb/next peter.chen-usb/ci-for-usb-next linus/master linux/master v5.6 next-20200403]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/vladimir-stankovic-displaylink-com/Add-MA-USB-Host-driver/20200328-042436
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing

If you fix the issue, kindly add following tag as appropriate
Reported-by: kbuild test robot <lkp@intel.com>


coccinelle warnings: (new ones prefixed by >>)

>> drivers/usb/mausb_host/utils.c:209:1-18: ERROR: nested lock+irqsave that reuses flags from line 201.
--
>> drivers/usb/mausb_host/hpal_events.c:86:46-49: ERROR: reference preceded by free on line 85

vim +209 drivers/usb/mausb_host/utils.c

   182	
   183	static ssize_t mausb_file_write(struct file *filp, const char __user *buffer,
   184					size_t size, loff_t *offset)
   185	{
   186		ssize_t num_of_bytes_to_write =
   187					sizeof(struct mausb_events_notification);
   188		struct mausb_events_notification notification;
   189		unsigned long flags;
   190		struct mausb_device *dev;
   191	
   192		if (size != (size_t)num_of_bytes_to_write) {
   193			mausb_pr_alert("Different expected bytes to write (%ld) from actual size (%ld)",
   194				       num_of_bytes_to_write, size);
   195			return MAUSB_DRIVER_WRITE_ERROR;
   196		}
   197	
   198		if (copy_from_user(&notification, buffer, size))
   199			return MAUSB_DRIVER_WRITE_ERROR;
   200	
 > 201		spin_lock_irqsave(&mss.lock, flags);
   202		dev = mausb_get_dev_from_addr_unsafe(notification.madev_addr);
   203	
   204		if (!dev) {
   205			spin_unlock_irqrestore(&mss.lock, flags);
   206			return 0;
   207		}
   208	
 > 209		spin_lock_irqsave(&dev->num_of_user_events_lock, flags);
   210		dev->num_of_user_events += notification.num_of_events;
   211		dev->num_of_completed_events += notification.num_of_completed_events;
   212		spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags);
   213	
   214		queue_work(dev->workq, &dev->work);
   215		spin_unlock_irqrestore(&mss.lock, flags);
   216	
   217		return num_of_bytes_to_write;
   218	}
   219	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers
  2020-03-27 16:37     ` Alan Stern
@ 2020-04-13 15:16       ` Vladimir Stankovic
  0 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-04-13 15:16 UTC (permalink / raw)
  To: Alan Stern; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On 27.3.20. 17:37, Alan Stern wrote:
> On Fri, 27 Mar 2020 vladimir.stankovic@displaylink.com wrote:
> 
>> Implemented handlers for subset of HCD events.
>>
>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
>> ---
>> +/*
>> + * For usb 2.0 logitech camera called multiple times, once during
>> + * enumeration of device and later after mausb_reset_device.
>> + */
>> +static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
> 
>> +/*
>> + * For usb 2.0 logitech camera called multiple times, once during enumeration
>> + * of device and later after mausb_reset_device. In latter case it is
>> + * required to address the device again in order for ep0 to work properly.
>> + */
>> +static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
> 
>> +/*
>> + * For usb 2.0 logitech camera called multiple times,
>> + * followed by either mausb_enable_device or mausb_address_device.
>> + * Resets device to non-addressed state.
>> + */
>> +static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
> 
> Why on earth do you have all these comments about Logitech cameras?  A 
> host controller driver shouldn't care about the type of devices it gets 
> connected to.
> 
> Alan Stern
> 
You're right. As it turns out, it is leftover during development.
Implementation has been tested with cameras from other vendors as well.

We'll remove those in next patch version.
 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v5 0/8] Add MA USB Host driver
  2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
                     ` (7 preceding siblings ...)
  2020-03-27 15:26   ` [PATCH v4 8/8] usb: mausb_host: Process MA-USB data packets vladimir.stankovic
@ 2020-04-25  9:19   ` vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
                       ` (9 more replies)
  8 siblings, 10 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Media Agnostic (MA) USB Host driver provides USB connectivity over an
available network, allowing host device to access remote USB devices
attached to one or more MA USB devices (accessible via network).

This driver has been developed to enable the host to communicate
with DisplayLink products supporting MA USB protocol (MA USB device,
in terms of MA USB Specification).

MA USB protocol used by MA USB Host driver has been implemented in
accordance with MA USB Specification Release 1.0b.

This driver depends on the functions provided by DisplayLink's
user-space driver.

v2:
- Fixed licensing info in headers
- Reorganized code to lower file count
- Patch has been split into 8 smaller patches

v3:
- Fixed nested spinlock usage
- Implemented IPv6 support

v4:
- No code changes
- Re-sent patch with different mail client config

v5:
- Updated kernel configuration with MA info
- Addressed build warnings for c6x architecture
- Fixed coccinelle warnings

Vladimir Stankovic (8):
  usb: Add MA-USB Host kernel module
  usb: mausb_host: Add link layer implementation
  usb: mausb_host: HCD initialization
  usb: mausb_host: Implement initial hub handlers
  usb: mausb_host: Introduce PAL processing
  usb: mausb_host: Add logic for PAL-to-PAL communication
  usb: mausb_host: MA-USB PAL events processing
  usb: mausb_host: Process MA-USB data packets

 MAINTAINERS                                  |    7 +
 drivers/usb/Kconfig                          |    2 +
 drivers/usb/Makefile                         |    2 +
 drivers/usb/mausb_host/Kconfig               |   15 +
 drivers/usb/mausb_host/Makefile              |   17 +
 drivers/usb/mausb_host/hcd.c                 | 1874 ++++++++++++++++
 drivers/usb/mausb_host/hcd.h                 |  162 ++
 drivers/usb/mausb_host/hpal.c                | 2082 ++++++++++++++++++
 drivers/usb/mausb_host/hpal.h                |  339 +++
 drivers/usb/mausb_host/hpal_data.c           |  714 ++++++
 drivers/usb/mausb_host/hpal_data.h           |   34 +
 drivers/usb/mausb_host/hpal_events.c         |  611 +++++
 drivers/usb/mausb_host/hpal_events.h         |   85 +
 drivers/usb/mausb_host/ip_link.c             |  374 ++++
 drivers/usb/mausb_host/ip_link.h             |   87 +
 drivers/usb/mausb_host/ma_usb.h              |  869 ++++++++
 drivers/usb/mausb_host/mausb_address.h       |   26 +
 drivers/usb/mausb_host/mausb_core.c          |  212 ++
 drivers/usb/mausb_host/mausb_driver_status.h |   17 +
 drivers/usb/mausb_host/mausb_event.h         |  224 ++
 drivers/usb/mausb_host/utils.c               |  358 +++
 drivers/usb/mausb_host/utils.h               |   45 +
 22 files changed, 8156 insertions(+)
 create mode 100644 drivers/usb/mausb_host/Kconfig
 create mode 100644 drivers/usb/mausb_host/Makefile
 create mode 100644 drivers/usb/mausb_host/hcd.c
 create mode 100644 drivers/usb/mausb_host/hcd.h
 create mode 100644 drivers/usb/mausb_host/hpal.c
 create mode 100644 drivers/usb/mausb_host/hpal.h
 create mode 100644 drivers/usb/mausb_host/hpal_data.c
 create mode 100644 drivers/usb/mausb_host/hpal_data.h
 create mode 100644 drivers/usb/mausb_host/hpal_events.c
 create mode 100644 drivers/usb/mausb_host/hpal_events.h
 create mode 100644 drivers/usb/mausb_host/ip_link.c
 create mode 100644 drivers/usb/mausb_host/ip_link.h
 create mode 100644 drivers/usb/mausb_host/ma_usb.h
 create mode 100644 drivers/usb/mausb_host/mausb_address.h
 create mode 100644 drivers/usb/mausb_host/mausb_core.c
 create mode 100644 drivers/usb/mausb_host/mausb_driver_status.h
 create mode 100644 drivers/usb/mausb_host/mausb_event.h
 create mode 100644 drivers/usb/mausb_host/utils.c
 create mode 100644 drivers/usb/mausb_host/utils.h


base-commit: ab51cac00ef2859f20a73d33a53f3a8987b65e11
-- 
2.17.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v5 1/8] usb: Add MA-USB Host kernel module
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-28 11:03       ` Greg KH
  2020-04-25  9:19     ` [PATCH v5 2/8] usb: mausb_host: Add link layer implementation vladimir.stankovic
                       ` (8 subsequent siblings)
  9 siblings, 1 reply; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Added utility macros, kernel device creation and cleanup, functions for
handling log formatting and a placeholder module for MA-USB Host device
driver.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 MAINTAINERS                         |  7 +++
 drivers/usb/Kconfig                 |  2 +
 drivers/usb/Makefile                |  2 +
 drivers/usb/mausb_host/Kconfig      | 15 +++++
 drivers/usb/mausb_host/Makefile     | 12 ++++
 drivers/usb/mausb_host/mausb_core.c | 90 +++++++++++++++++++++++++++++
 drivers/usb/mausb_host/utils.c      | 85 +++++++++++++++++++++++++++
 drivers/usb/mausb_host/utils.h      | 40 +++++++++++++
 8 files changed, 253 insertions(+)
 create mode 100644 drivers/usb/mausb_host/Kconfig
 create mode 100644 drivers/usb/mausb_host/Makefile
 create mode 100644 drivers/usb/mausb_host/mausb_core.c
 create mode 100644 drivers/usb/mausb_host/utils.c
 create mode 100644 drivers/usb/mausb_host/utils.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 453fe0713e68..8b63b246ba67 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10352,6 +10352,13 @@ W:	https://linuxtv.org
 T:	git git://linuxtv.org/media_tree.git
 F:	drivers/media/radio/radio-maxiradio*
 
+MEDIA AGNOSTIC (MA) USB HOST DRIVER
+M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
+L:	mausb-host-devel@displaylink.com
+W:	https://www.displaylink.com
+S:	Maintained
+F:	drivers/usb/mausb_host/*
+
 MCAN MMIO DEVICE DRIVER
 M:	Dan Murphy <dmurphy@ti.com>
 M:	Sriram Dash <sriram.dash@samsung.com>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 275568abc670..4e92f1fa0fa5 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -164,6 +164,8 @@ source "drivers/usb/misc/Kconfig"
 
 source "drivers/usb/atm/Kconfig"
 
+source "drivers/usb/mausb_host/Kconfig"
+
 endif # USB
 
 source "drivers/usb/phy/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 1c1c1d659394..22d1998db0e2 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -66,3 +66,5 @@ obj-$(CONFIG_USBIP_CORE)	+= usbip/
 obj-$(CONFIG_TYPEC)		+= typec/
 
 obj-$(CONFIG_USB_ROLE_SWITCH)	+= roles/
+
+obj-$(CONFIG_HOST_MAUSB)        += mausb_host/
diff --git a/drivers/usb/mausb_host/Kconfig b/drivers/usb/mausb_host/Kconfig
new file mode 100644
index 000000000000..a8363e7e8f97
--- /dev/null
+++ b/drivers/usb/mausb_host/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel configuration file for MA-USB Host driver.
+#
+# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+#
+
+config HOST_MAUSB
+	tristate "Media Agnostic (MA) USB Host Driver"
+	depends on USB=y
+	help
+	  This is a Media Agnostic (MA) USB Host driver which enables host
+	  communication via MA USB protocol stack.
+
+	  If this driver is compiled as a module, it will be named mausb_host.
diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
new file mode 100644
index 000000000000..2e353fa0958b
--- /dev/null
+++ b/drivers/usb/mausb_host/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DisplayLink MA-USB Host driver.
+#
+# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+#
+
+obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
+mausb_host-y := mausb_core.o
+mausb_host-y += utils.o
+
+ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
new file mode 100644
index 000000000000..8638dd0a4856
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/net.h>
+
+#include "utils.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("DisplayLink (UK) Ltd.");
+MODULE_VERSION(MAUSB_DRIVER_VERSION);
+
+static int mausb_client_connect(const char *value,
+				const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mausb_client_disconnect(const char *value,
+				   const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mausb_device_connect(const char *value,
+				const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static int mausb_device_disconnect(const char *value,
+				   const struct kernel_param *kp)
+{
+	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
+
+	return 0;
+}
+
+static const struct kernel_param_ops mausb_device_connect_ops = {
+	.set = mausb_device_connect
+};
+
+static const struct kernel_param_ops mausb_device_disconnect_ops = {
+	.set = mausb_device_disconnect
+};
+
+static const struct kernel_param_ops mausb_client_connect_ops = {
+	.set = mausb_client_connect
+};
+
+static const struct kernel_param_ops mausb_client_disconnect_ops = {
+	.set = mausb_client_disconnect
+};
+
+static int mausb_host_init(void)
+{
+	int status = mausb_create_dev();
+
+	mausb_pr_info("Module load. Version=%s", MAUSB_DRIVER_VERSION);
+
+	if (status < 0) {
+		mausb_pr_alert("Failed to load MAUSB module!");
+		return status;
+	}
+
+	return 0;
+}
+
+static void mausb_host_exit(void)
+{
+	mausb_pr_info("Module unloading started...");
+	mausb_cleanup_dev(1);
+	mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION);
+}
+
+module_init(mausb_host_init);
+module_exit(mausb_host_exit);
diff --git a/drivers/usb/mausb_host/utils.c b/drivers/usb/mausb_host/utils.c
new file mode 100644
index 000000000000..c055b578e402
--- /dev/null
+++ b/drivers/usb/mausb_host/utils.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "utils.h"
+
+#include <linux/atomic.h>
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#define MAUSB_KERNEL_DEV_NAME "mausb_host"
+#define MAUSB_READ_DEVICE_TIMEOUT_MS 500
+
+static dev_t mausb_major_kernel;
+static struct cdev  mausb_kernel_dev;
+static struct class *mausb_kernel_class;
+
+static int mausb_file_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static int mausb_file_close(struct inode *inode, struct file *filp)
+{
+	kfree(filp->private_data);
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static const struct file_operations mausb_file_ops = {
+	.open	 = mausb_file_open,
+	.release = mausb_file_close,
+};
+
+int mausb_create_dev(void)
+{
+	int device_created = 0;
+	int status = alloc_chrdev_region(&mausb_major_kernel, 0, 1,
+					 MAUSB_KERNEL_DEV_NAME "_proc");
+	if (status)
+		goto cleanup;
+
+	mausb_kernel_class = class_create(THIS_MODULE,
+					  MAUSB_KERNEL_DEV_NAME "_sys");
+	if (!mausb_kernel_class) {
+		status = -ENOMEM;
+		goto cleanup;
+	}
+
+	if (!device_create(mausb_kernel_class, NULL, mausb_major_kernel, NULL,
+			   MAUSB_KERNEL_DEV_NAME "_dev")) {
+		status = -ENOMEM;
+		goto cleanup;
+	}
+	device_created = 1;
+	cdev_init(&mausb_kernel_dev, &mausb_file_ops);
+	status = cdev_add(&mausb_kernel_dev, mausb_major_kernel, 1);
+	if (status)
+		goto cleanup;
+	return 0;
+cleanup:
+	mausb_cleanup_dev(device_created);
+	return status;
+}
+
+void mausb_cleanup_dev(int device_created)
+{
+	if (device_created) {
+		device_destroy(mausb_kernel_class, mausb_major_kernel);
+		cdev_del(&mausb_kernel_dev);
+	}
+
+	if (mausb_kernel_class)
+		class_destroy(mausb_kernel_class);
+
+	unregister_chrdev_region(mausb_major_kernel, 1);
+}
diff --git a/drivers/usb/mausb_host/utils.h b/drivers/usb/mausb_host/utils.h
new file mode 100644
index 000000000000..9adf4122e64d
--- /dev/null
+++ b/drivers/usb/mausb_host/utils.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_UTILS_H__
+#define __MAUSB_UTILS_H__
+
+#if defined(MAUSB_NO_LOGS)
+#define mausb_pr_logs(...)
+#else
+#include <linux/printk.h>
+#include <linux/sched.h>
+#define mausb_pr_logs(level_str, level, log, ...)\
+	pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
+	current->pid, current->tgid, __func__, ##__VA_ARGS__)
+#endif /* MAUSB_NO_LOGS */
+
+#define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
+
+#define mausb_pr_err(...) mausb_pr_logs(err, 2, ##__VA_ARGS__)
+
+#define mausb_pr_warn(...) mausb_pr_logs(warn, 3, ##__VA_ARGS__)
+
+#define mausb_pr_info(...) mausb_pr_logs(info, 4, ##__VA_ARGS__)
+
+#if defined(MAUSB_LOG_VERBOSE)
+	#define mausb_pr_debug(...) mausb_pr_logs(debug, 5, ##__VA_ARGS__)
+#else
+	#define mausb_pr_debug(...)
+#endif /* defined(MAUSB_LOG_VERBOSE) */
+
+#define MAUSB_STRINGIFY2(x) #x
+#define MAUSB_STRINGIFY(x) MAUSB_STRINGIFY2(x)
+
+#define MAUSB_DRIVER_VERSION MAUSB_STRINGIFY(1.3.0.0.6f5beb53)
+
+int mausb_create_dev(void);
+void mausb_cleanup_dev(int device_created);
+
+#endif /* __MAUSB_UTILS_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 2/8] usb: mausb_host: Add link layer implementation
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
                       ` (7 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented link layer using kernel sockets. Link layer
manages network communication and provides interface for upper
layers of MA-USB stack.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile  |   1 +
 drivers/usb/mausb_host/ip_link.c | 374 +++++++++++++++++++++++++++++++
 drivers/usb/mausb_host/ip_link.h |  87 +++++++
 3 files changed, 462 insertions(+)
 create mode 100644 drivers/usb/mausb_host/ip_link.c
 create mode 100644 drivers/usb/mausb_host/ip_link.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index 2e353fa0958b..19445b73b50b 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -8,5 +8,6 @@
 obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
 mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
+mausb_host-y += ip_link.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/ip_link.c b/drivers/usb/mausb_host/ip_link.c
new file mode 100644
index 000000000000..49b592c02210
--- /dev/null
+++ b/drivers/usb/mausb_host/ip_link.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "ip_link.h"
+
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/workqueue.h>
+#include <net/sock.h>
+#include <net/tcp.h>
+
+#include "utils.h"
+
+static void __mausb_ip_connect(struct work_struct *work);
+static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx);
+static void __mausb_ip_recv_work(struct work_struct *work);
+static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx
+					     *recv_ctx);
+static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx
+					    *recv_ctx);
+
+int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx,
+		      struct net *net_ns,
+		      char ip_addr[INET6_ADDRSTRLEN],
+		      u16 port, void *context,
+		      void (*fn_callback)(void *ctx, enum mausb_channel channel,
+					  enum mausb_link_action act,
+					  int status, void *data),
+		      enum mausb_channel channel)
+{
+	struct mausb_ip_ctx *ctx;
+
+	*ip_ctx = kzalloc(sizeof(**ip_ctx), GFP_ATOMIC);
+	if (!*ip_ctx)
+		return -ENOMEM;
+
+	ctx = *ip_ctx;
+	ctx->client_socket = NULL;
+	__mausb_ip_recv_ctx_clear(&ctx->recv_ctx);
+
+	if (in4_pton(ip_addr, -1,
+		     (u8 *)&ctx->dev_addr_in.sa_in.sin_addr.s_addr, -1,
+		     NULL) == 1) {
+		ctx->dev_addr_in.sa_in.sin_family = AF_INET;
+		ctx->dev_addr_in.sa_in.sin_port = htons(port);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (in6_pton(ip_addr, -1,
+			    (u8 *)&ctx->dev_addr_in.sa_in6.sin6_addr.in6_u, -1,
+			    NULL) == 1) {
+		ctx->dev_addr_in.sa_in6.sin6_family = AF_INET6;
+		ctx->dev_addr_in.sa_in6.sin6_port = htons(port);
+#endif
+	} else {
+		mausb_pr_err("Invalid IP address received: address=%s",
+			     ip_addr);
+		kfree(ctx);
+		return -EINVAL;
+	}
+
+	ctx->net_ns = net_ns;
+
+	if (channel == MAUSB_ISOCH_CHANNEL)
+		ctx->udp = true;
+
+	ctx->connect_workq = alloc_ordered_workqueue("connect_workq",
+						     WQ_MEM_RECLAIM);
+	if (!ctx->connect_workq) {
+		kfree(ctx);
+		return -ENOMEM;
+	}
+
+	ctx->recv_workq = alloc_ordered_workqueue("recv_workq", WQ_MEM_RECLAIM);
+	if (!ctx->recv_workq) {
+		destroy_workqueue(ctx->connect_workq);
+		kfree(ctx);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&ctx->connect_work, __mausb_ip_connect);
+	INIT_WORK(&ctx->recv_work, __mausb_ip_recv_work);
+
+	ctx->channel	 = channel;
+	ctx->ctx	 = context;
+	ctx->fn_callback = fn_callback;
+
+	return 0;
+}
+
+void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx)
+{
+	if (!ip_ctx)
+		return;
+
+	if (ip_ctx->connect_workq) {
+		flush_workqueue(ip_ctx->connect_workq);
+		destroy_workqueue(ip_ctx->connect_workq);
+	}
+
+	if (ip_ctx->recv_workq) {
+		flush_workqueue(ip_ctx->recv_workq);
+		destroy_workqueue(ip_ctx->recv_workq);
+	}
+	if (ip_ctx->client_socket)
+		sock_release(ip_ctx->client_socket);
+	__mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx);
+
+	kfree(ip_ctx);
+}
+
+static void __mausb_ip_set_options(struct socket *sock, bool udp)
+{
+	u32 optval = 0;
+	unsigned int optlen = sizeof(optval);
+	int status = 0;
+	struct __kernel_sock_timeval timeo = {.tv_sec = 0, .tv_usec = 500000U };
+	struct __kernel_sock_timeval send_timeo = {.tv_sec = 1, .tv_usec = 0 };
+
+	if (!udp) {
+		optval = 1;
+		status = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+					   (char *)&optval, optlen);
+		if (status < 0)
+			mausb_pr_warn("Failed to set tcp no delay option: status=%d",
+				      status);
+	}
+
+	status = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_NEW,
+				   (char *)&timeo, sizeof(timeo));
+	if (status < 0)
+		mausb_pr_warn("Failed to set recv timeout option: status=%d",
+			      status);
+
+	status = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_NEW,
+				   (char *)&send_timeo, sizeof(send_timeo));
+	if (status < 0)
+		mausb_pr_warn("Failed to set snd timeout option: status=%d",
+			      status);
+
+	optval = MAUSB_LINK_BUFF_SIZE;
+	status  = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		mausb_pr_warn("Failed to set recv buffer size: status=%d",
+			      status);
+
+	optval = MAUSB_LINK_BUFF_SIZE;
+	status  = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		mausb_pr_warn("Failed to set send buffer size: status=%d",
+			      status);
+
+	optval = MAUSB_LINK_TOS_LEVEL_EF;
+	status  = kernel_setsockopt(sock, IPPROTO_IP, IP_TOS,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		mausb_pr_warn("Failed to set QOS: status=%d", status);
+}
+
+static void __mausb_ip_connect(struct work_struct *work)
+{
+	int status = 0;
+	struct sockaddr *sa;
+	int sa_size;
+	struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx,
+						   connect_work);
+	unsigned short int family = ip_ctx->dev_addr_in.sa_in.sin_family;
+
+	if (!ip_ctx->udp) {
+		status = sock_create_kern(ip_ctx->net_ns, family, SOCK_STREAM,
+					  IPPROTO_TCP, &ip_ctx->client_socket);
+		if (status < 0) {
+			mausb_pr_err("Failed to create socket: status=%d",
+				     status);
+			goto callback;
+		}
+	} else {
+		status = sock_create_kern(ip_ctx->net_ns, family, SOCK_DGRAM,
+					  IPPROTO_UDP, &ip_ctx->client_socket);
+		if (status < 0) {
+			mausb_pr_err("Failed to create socket: status=%d",
+				     status);
+			goto callback;
+		}
+	}
+
+	__mausb_ip_set_options((struct socket *)ip_ctx->client_socket,
+			       ip_ctx->udp);
+
+	if (family == AF_INET) {
+		sa = (struct sockaddr *)&ip_ctx->dev_addr_in.sa_in;
+		sa_size = sizeof(ip_ctx->dev_addr_in.sa_in);
+		mausb_pr_info("Connecting to %pI4:%d, status=%d",
+			      &ip_ctx->dev_addr_in.sa_in.sin_addr,
+			      htons(ip_ctx->dev_addr_in.sa_in.sin_port),
+			      status);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (family == AF_INET6) {
+		sa = (struct sockaddr *)&ip_ctx->dev_addr_in.sa_in6;
+		sa_size = sizeof(ip_ctx->dev_addr_in.sa_in6);
+		mausb_pr_info("Connecting to %pI6c:%d, status=%d",
+			      &ip_ctx->dev_addr_in.sa_in6.sin6_addr,
+			      htons(ip_ctx->dev_addr_in.sa_in6.sin6_port),
+			      status);
+#endif
+	} else {
+		mausb_pr_err("Wrong network family provided");
+		status = -EINVAL;
+		goto callback;
+	}
+
+	status = kernel_connect(ip_ctx->client_socket, sa, sa_size, O_RDWR);
+	if (status < 0) {
+		mausb_pr_err("Failed to connect to host, status=%d", status);
+		goto clear_socket;
+	}
+
+	queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work);
+
+	goto callback;
+
+clear_socket:
+	sock_release(ip_ctx->client_socket);
+	ip_ctx->client_socket = NULL;
+callback:
+	ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel, MAUSB_LINK_CONNECT,
+			    status, NULL);
+}
+
+void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx)
+{
+	queue_work(ip_ctx->connect_workq, &ip_ctx->connect_work);
+}
+
+int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx)
+{
+	if (ip_ctx && ip_ctx->client_socket)
+		return kernel_sock_shutdown(ip_ctx->client_socket, SHUT_RDWR);
+	return 0;
+}
+
+int mausb_ip_send(struct mausb_ip_ctx *ip_ctx,
+		  struct mausb_kvec_data_wrapper *wrapper)
+{
+	struct msghdr msghd;
+
+	if (!ip_ctx) {
+		mausb_pr_alert("Socket ctx is NULL!");
+		return -EINVAL;
+	}
+
+	memset(&msghd, 0, sizeof(msghd));
+	msghd.msg_flags = MSG_WAITALL;
+
+	return kernel_sendmsg(ip_ctx->client_socket, &msghd, wrapper->kvec,
+			      wrapper->kvec_num, wrapper->length);
+}
+
+static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx *recv_ctx)
+{
+	recv_ctx->buffer   = NULL;
+	recv_ctx->left	   = 0;
+	recv_ctx->received = 0;
+}
+
+static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx *recv_ctx)
+{
+	kfree(recv_ctx->buffer);
+	__mausb_ip_recv_ctx_clear(recv_ctx);
+}
+
+static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx)
+{
+	struct msghdr msghd;
+	struct kvec vec;
+	int  status;
+	bool peek = true;
+	unsigned int optval = 1;
+	struct socket *client_socket = (struct socket *)ip_ctx->client_socket;
+
+	/* receive with timeout of 0.5s */
+	while (true) {
+		memset(&msghd, 0, sizeof(msghd));
+		if (peek) {
+			vec.iov_base = ip_ctx->recv_ctx.common_hdr;
+			vec.iov_len  = sizeof(ip_ctx->recv_ctx.common_hdr);
+			msghd.msg_flags = MSG_PEEK;
+		} else {
+			vec.iov_base =
+			    ip_ctx->recv_ctx.buffer +
+			    ip_ctx->recv_ctx.received;
+			vec.iov_len = ip_ctx->recv_ctx.left;
+			msghd.msg_flags = MSG_WAITALL;
+		}
+
+		if (!ip_ctx->udp) {
+			status = kernel_setsockopt(client_socket, IPPROTO_TCP,
+						   TCP_QUICKACK,
+						   (char *)&optval,
+						   sizeof(optval));
+			if (status != 0) {
+				mausb_pr_warn("Setting TCP_QUICKACK failed, status=%d",
+					      status);
+			}
+		}
+
+		status = kernel_recvmsg(client_socket, &msghd, &vec, 1,
+					vec.iov_len, (int)msghd.msg_flags);
+		if (status == -EAGAIN) {
+			return -EAGAIN;
+		} else if (status <= 0) {
+			mausb_pr_warn("kernel_recvmsg, status=%d", status);
+
+			__mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx);
+			ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel,
+					    MAUSB_LINK_RECV, status, NULL);
+			return status;
+		}
+
+		mausb_pr_debug("kernel_recvmsg, status=%d", status);
+
+		if (peek) {
+			if ((unsigned int)status <
+					sizeof(ip_ctx->recv_ctx.common_hdr))
+				return -EAGAIN;
+			/* length field of mausb_common_hdr */
+			ip_ctx->recv_ctx.left =
+			    *(u16 *)(&ip_ctx->recv_ctx.common_hdr[2]);
+			ip_ctx->recv_ctx.received = 0;
+			ip_ctx->recv_ctx.buffer	  =
+			    kzalloc(ip_ctx->recv_ctx.left, GFP_KERNEL);
+			peek = false;
+			if (!ip_ctx->recv_ctx.buffer) {
+				ip_ctx->fn_callback(ip_ctx->ctx,
+						    ip_ctx->channel,
+						    MAUSB_LINK_RECV,
+						    -ENOMEM, NULL);
+				return -ENOMEM;
+			}
+		} else {
+			if (status < ip_ctx->recv_ctx.left) {
+				ip_ctx->recv_ctx.left -= (u16)status;
+				ip_ctx->recv_ctx.received += (u16)status;
+			} else {
+				ip_ctx->fn_callback(ip_ctx->ctx,
+						    ip_ctx->channel,
+						    MAUSB_LINK_RECV, status,
+						    ip_ctx->recv_ctx.buffer);
+				__mausb_ip_recv_ctx_clear(&ip_ctx->recv_ctx);
+				peek = true;
+			}
+		}
+	}
+
+	return status;
+}
+
+static void __mausb_ip_recv_work(struct work_struct *work)
+{
+	struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx,
+						   recv_work);
+	int status = __mausb_ip_recv(ip_ctx);
+
+	if (status <= 0 && status != -EAGAIN)
+		return;
+
+	queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work);
+}
diff --git a/drivers/usb/mausb_host/ip_link.h b/drivers/usb/mausb_host/ip_link.h
new file mode 100644
index 000000000000..5946151e4e4e
--- /dev/null
+++ b/drivers/usb/mausb_host/ip_link.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_IP_LINK_H__
+#define __MAUSB_IP_LINK_H__
+
+#include <linux/inet.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+#include <net/net_namespace.h>
+
+#define MAUSB_LINK_BUFF_SIZE	16777216
+#define MAUSB_LINK_TOS_LEVEL_EF 0xB8
+
+enum mausb_link_action {
+	MAUSB_LINK_CONNECT	= 0,
+	MAUSB_LINK_DISCONNECT	= 1,
+	MAUSB_LINK_RECV		= 2,
+	MAUSB_LINK_SEND		= 3
+};
+
+enum mausb_channel {
+	MAUSB_CTRL_CHANNEL  = 0,
+	MAUSB_ISOCH_CHANNEL = 1,
+	MAUSB_BULK_CHANNEL  = 2,
+	MAUSB_INTR_CHANNEL  = 3,
+	MAUSB_MGMT_CHANNEL  = 4
+};
+
+struct mausb_kvec_data_wrapper {
+	struct kvec *kvec;
+	u32    kvec_num;
+	u32    length;
+};
+
+struct mausb_ip_recv_ctx {
+	u16  left;
+	u16  received;
+	char *buffer;
+	char common_hdr[12] __aligned(4);
+};
+
+struct mausb_ip_ctx {
+	struct socket		*client_socket;
+	union {
+		struct sockaddr_in sa_in;
+#if IS_ENABLED(CONFIG_IPV6)
+		struct sockaddr_in6 sa_in6;
+#endif
+	} dev_addr_in;
+	struct net		*net_ns;
+	bool			udp;
+
+	/* Queues to schedule rx work */
+	struct workqueue_struct	*recv_workq;
+	struct workqueue_struct	*connect_workq;
+	struct work_struct	recv_work;
+	struct work_struct	connect_work;
+
+	struct mausb_ip_recv_ctx recv_ctx; /* recv buffer */
+
+	enum mausb_channel channel;
+	void *ctx;
+	/* callback should store task into hpal queue */
+	void (*fn_callback)(void *ctx, enum mausb_channel channel,
+			    enum mausb_link_action act, int status, void *data);
+};
+
+int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx,
+		      struct net *net_ns,
+		      char ip_addr[INET6_ADDRSTRLEN],
+		      u16 port,
+		      void *ctx,
+		      void (*ctx_callback)(void *ctx,
+					   enum mausb_channel channel,
+					   enum mausb_link_action act,
+					   int status, void *data),
+		      enum mausb_channel channel);
+int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx);
+int mausb_ip_send(struct mausb_ip_ctx *ip_ctx,
+		  struct mausb_kvec_data_wrapper *wrapper);
+
+void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx);
+void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx);
+
+#endif /* __MAUSB_IP_LINK_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 3/8] usb: mausb_host: HCD initialization
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 2/8] usb: mausb_host: Add link layer implementation vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-28 11:07       ` Greg KH
  2020-04-25  9:19     ` [PATCH v5 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
                       ` (6 subsequent siblings)
  9 siblings, 1 reply; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented HCD initialization/deinitialization functionality.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile     |   1 +
 drivers/usb/mausb_host/hcd.c        | 188 ++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hcd.h        |  71 +++++++++++
 drivers/usb/mausb_host/mausb_core.c |  20 ++-
 4 files changed, 275 insertions(+), 5 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hcd.c
 create mode 100644 drivers/usb/mausb_host/hcd.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index 19445b73b50b..cce4696682b2 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
 mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
 mausb_host-y += ip_link.o
+mausb_host-y += hcd.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
new file mode 100644
index 000000000000..3aa548a6cb30
--- /dev/null
+++ b/drivers/usb/mausb_host/hcd.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hcd.h"
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+
+#include "utils.h"
+
+static int mausb_open(struct inode *inode, struct file *file);
+static int mausb_release(struct inode *inode, struct file *file);
+static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length,
+			  loff_t *offset);
+static ssize_t mausb_write(struct file *file, const char __user *buffer,
+			   size_t length, loff_t *offset);
+static long mausb_ioctl(struct file *file, unsigned int ioctl_func,
+			unsigned long ioctl_buffer);
+static int mausb_bus_probe(struct device *dev);
+static int mausb_bus_remove(struct device *dev);
+static int mausb_bus_match(struct device *dev, struct device_driver *drv);
+
+static const struct file_operations mausb_fops = {
+	.open		= mausb_open,
+	.release	= mausb_release,
+	.read		= mausb_read,
+	.write		= mausb_write,
+	.unlocked_ioctl	= mausb_ioctl
+};
+
+static unsigned int major;
+static unsigned int minor = 1;
+static dev_t devt;
+static struct device *device;
+
+struct mausb_hcd	*mhcd;
+static struct class	*mausb_class;
+static struct bus_type	mausb_bus_type = {
+	.name	= DEVICE_NAME,
+	.match	= mausb_bus_match,
+	.probe	= mausb_bus_probe,
+	.remove	= mausb_bus_remove,
+};
+
+static struct device_driver mausb_driver = {
+	.name	= DEVICE_NAME,
+	.bus	= &mausb_bus_type,
+	.owner	= THIS_MODULE,
+};
+
+static void mausb_remove(void)
+{
+	struct usb_hcd *hcd, *shared_hcd;
+
+	hcd	   = mhcd->hcd_hs_ctx->hcd;
+	shared_hcd = mhcd->hcd_ss_ctx->hcd;
+
+	if (shared_hcd) {
+		usb_remove_hcd(shared_hcd);
+		usb_put_hcd(shared_hcd);
+		mhcd->hcd_ss_ctx = NULL;
+	}
+
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+	mhcd->hcd_hs_ctx = NULL;
+}
+
+static int mausb_bus_probe(struct device *dev)
+{
+	return 0;
+}
+
+static int mausb_bus_remove(struct device *dev)
+{
+	return 0;
+}
+
+static int mausb_bus_match(struct device *dev, struct device_driver *drv)
+{
+	if (strncmp(dev->bus->name, drv->name, strlen(drv->name)))
+		return 0;
+	else
+		return 1;
+}
+
+static int mausb_open(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static int mausb_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length,
+			  loff_t *offset)
+{
+	return 0;
+}
+
+static ssize_t mausb_write(struct file *file, const char __user *buffer,
+			   size_t length, loff_t *offset)
+{
+	return 0;
+}
+
+static long mausb_ioctl(struct file *file, unsigned int ioctl_func,
+			unsigned long ioctl_buffer)
+{
+	return 0;
+}
+
+int mausb_init_hcd(void)
+{
+	int retval;
+
+	retval = register_chrdev(0, DEVICE_NAME, &mausb_fops);
+	if (retval < 0) {
+		mausb_pr_err("Register_chrdev failed");
+		return retval;
+	}
+
+	major = (unsigned int)retval;
+	retval = bus_register(&mausb_bus_type);
+	if (retval) {
+		mausb_pr_err("Bus_register failed %d", retval);
+		goto bus_register_error;
+	}
+
+	mausb_class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(mausb_class)) {
+		mausb_pr_err("Class_create failed %ld", PTR_ERR(mausb_class));
+		goto class_error;
+	}
+
+	retval = driver_register(&mausb_driver);
+	if (retval) {
+		mausb_pr_err("Driver_register failed");
+		goto driver_register_error;
+	}
+
+	mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC);
+	if (!mhcd) {
+		retval = -ENOMEM;
+		goto mausb_hcd_alloc_failed;
+	}
+
+	devt = MKDEV(major, minor);
+	device = device_create(mausb_class, NULL, devt, mhcd, DEVICE_NAME);
+	if (IS_ERR(device)) {
+		mausb_pr_err("Device_create failed %ld", PTR_ERR(device));
+		goto device_create_error;
+	}
+
+	device->driver = &mausb_driver;
+
+	return retval;
+device_create_error:
+	kfree(mhcd);
+	mhcd = NULL;
+mausb_hcd_alloc_failed:
+	driver_unregister(&mausb_driver);
+driver_register_error:
+	class_destroy(mausb_class);
+class_error:
+	bus_unregister(&mausb_bus_type);
+bus_register_error:
+	unregister_chrdev(major, DEVICE_NAME);
+
+	return retval;
+}
+
+void mausb_deinit_hcd(void)
+{
+	if (mhcd) {
+		mausb_remove();
+		device_destroy(mausb_class, devt);
+		driver_unregister(&mausb_driver);
+		class_destroy(mausb_class);
+		bus_unregister(&mausb_bus_type);
+		unregister_chrdev(major, DEVICE_NAME);
+	}
+}
diff --git a/drivers/usb/mausb_host/hcd.h b/drivers/usb/mausb_host/hcd.h
new file mode 100644
index 000000000000..cac62ba1f1e2
--- /dev/null
+++ b/drivers/usb/mausb_host/hcd.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HCD_H__
+#define __MAUSB_HCD_H__
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/proc_fs.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#define DEVICE_NAME "mausb_host_hcd_dev"
+#define CLASS_NAME "mausb"
+
+#define NUMBER_OF_PORTS		15
+
+#define MAX_USB_DEVICE_DEPTH	6
+
+#define RESPONSE_TIMEOUT	5000
+
+#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
+#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
+
+enum mausb_device_type {
+	USBDEVICE = 0,
+	USB20HUB  = 1,
+	USB30HUB  = 2
+};
+
+enum mausb_device_speed {
+	LOW_SPEED	 = 0,
+	FULL_SPEED	 = 1,
+	HIGH_SPEED	 = 2,
+	SUPER_SPEED	 = 3,
+	SUPER_SPEED_PLUS = 4
+};
+
+struct mausb_hcd {
+	spinlock_t	lock;	/* Protect HCD during URB processing */
+	struct device	*pdev;
+	u16		connected_ports;
+
+	struct rb_root	mausb_urbs;
+	struct hub_ctx	*hcd_ss_ctx;
+	struct hub_ctx	*hcd_hs_ctx;
+	struct notifier_block power_state_listener;
+};
+
+struct mausb_dev {
+	u32		port_status;
+	struct rb_root	usb_devices;
+	u8		dev_speed;
+	void		*ma_dev;
+};
+
+struct hub_ctx {
+	struct mausb_hcd *mhcd;
+	struct usb_hcd	 *hcd;
+	struct mausb_dev ma_devs[NUMBER_OF_PORTS];
+};
+
+int mausb_init_hcd(void);
+void mausb_deinit_hcd(void);
+
+#endif /* __MAUSB_HCD_H__ */
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
index 8638dd0a4856..3ce90c29f6de 100644
--- a/drivers/usb/mausb_host/mausb_core.c
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -11,6 +11,7 @@
 #include <linux/moduleparam.h>
 #include <linux/net.h>
 
+#include "hcd.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
@@ -67,21 +68,30 @@ static const struct kernel_param_ops mausb_client_disconnect_ops = {
 
 static int mausb_host_init(void)
 {
-	int status = mausb_create_dev();
+	int status;
 
 	mausb_pr_info("Module load. Version=%s", MAUSB_DRIVER_VERSION);
+	status = mausb_init_hcd();
+	if (status < 0)
+		goto cleanup;
 
-	if (status < 0) {
-		mausb_pr_alert("Failed to load MAUSB module!");
-		return status;
-	}
+	status = mausb_create_dev();
+	if (status < 0)
+		goto cleanup_hcd;
 
 	return 0;
+
+cleanup_hcd:
+	mausb_deinit_hcd();
+cleanup:
+	mausb_pr_alert("Failed to load MAUSB module!");
+	return status;
 }
 
 static void mausb_host_exit(void)
 {
 	mausb_pr_info("Module unloading started...");
+	mausb_deinit_hcd();
 	mausb_cleanup_dev(1);
 	mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION);
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 4/8] usb: mausb_host: Implement initial hub handlers
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (2 preceding siblings ...)
  2020-04-25  9:19     ` [PATCH v5 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
                       ` (5 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented handlers for subset of HCD events.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/hcd.c | 948 ++++++++++++++++++++++++++++++++++-
 drivers/usb/mausb_host/hcd.h |  86 +++-
 2 files changed, 1030 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
index 3aa548a6cb30..d8ede5b33fb4 100644
--- a/drivers/usb/mausb_host/hcd.c
+++ b/drivers/usb/mausb_host/hcd.c
@@ -71,7 +71,7 @@ static void mausb_remove(void)
 
 static int mausb_bus_probe(struct device *dev)
 {
-	return 0;
+	return mausb_probe(dev);
 }
 
 static int mausb_bus_remove(struct device *dev)
@@ -159,7 +159,15 @@ int mausb_init_hcd(void)
 
 	device->driver = &mausb_driver;
 
+	retval = mausb_probe(device);
+	if (retval) {
+		mausb_pr_err("Mausb_probe failed");
+		goto mausb_probe_failed;
+	}
+
 	return retval;
+mausb_probe_failed:
+	device_destroy(mausb_class, devt);
 device_create_error:
 	kfree(mhcd);
 	mhcd = NULL;
@@ -186,3 +194,941 @@ void mausb_deinit_hcd(void)
 		unregister_chrdev(major, DEVICE_NAME);
 	}
 }
+
+static const char driver_name[] = "MA-USB host controller";
+
+static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length);
+static void mausb_set_port_feature(struct usb_hcd *hcd, u16 type_req,
+				   u16 value, u16 index, char *buff,
+				   u16 length);
+static void mausb_get_port_status(struct usb_hcd *hcd, u16 type_req,
+				  u16 value, u16 index, char *buff,
+				  u16 length);
+static void mausb_clear_port_feature(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length);
+static void mausb_get_hub_status(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length);
+static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			      struct usb_host_endpoint *endpoint);
+static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			       struct usb_host_endpoint *endpoint);
+static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev);
+static void mausb_endpoint_disable(struct usb_hcd *hcd,
+				   struct usb_host_endpoint *endpoint);
+static void mausb_endpoint_reset(struct usb_hcd *hcd,
+				 struct usb_host_endpoint *endpoint);
+static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_hcd_bus_resume(struct usb_hcd *hcd);
+static int mausb_hcd_bus_suspend(struct usb_hcd *hcd);
+static int mausb_hcd_get_frame_number(struct usb_hcd *hcd);
+static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length);
+static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff);
+static int mausb_hcd_reset(struct usb_hcd *hcd);
+static int mausb_hcd_start(struct usb_hcd *hcd);
+static void mausb_hcd_stop(struct usb_hcd *hcd);
+static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
+				   struct usb_tt *tt, gfp_t mem_flags);
+static void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
+
+static const struct hc_driver mausb_hc_driver = {
+	.description  =  driver_name,
+	.product_desc = driver_name,
+	.flags	      = HCD_USB3 | HCD_SHARED,
+
+	.hcd_priv_size = sizeof(struct hub_ctx),
+
+	.reset = mausb_hcd_reset,
+	.start = mausb_hcd_start,
+	.stop  = mausb_hcd_stop,
+
+	.get_frame_number = mausb_hcd_get_frame_number,
+
+	.hub_status_data   = mausb_hcd_hub_status,
+	.hub_control	   = mausb_hcd_hub_control,
+	.update_hub_device = mausb_hub_update_device,
+	.bus_suspend	   = mausb_hcd_bus_suspend,
+	.bus_resume	   = mausb_hcd_bus_resume,
+
+	.alloc_dev	= mausb_alloc_dev,
+	.free_dev	= mausb_free_dev,
+	.enable_device	= mausb_enable_device,
+	.update_device	= mausb_update_device,
+	.reset_device	= mausb_reset_device,
+
+	.add_endpoint	  = mausb_add_endpoint,
+	.drop_endpoint	  = mausb_drop_endpoint,
+	.check_bandwidth  = mausb_check_bandwidth,
+	.reset_bandwidth  = mausb_reset_bandwidth,
+	.address_device   = mausb_address_device,
+	.endpoint_disable = mausb_endpoint_disable,
+	.endpoint_reset	  = mausb_endpoint_reset,
+};
+
+static struct {
+	struct usb_bos_descriptor    bos;
+	struct usb_ss_cap_descriptor ss_cap;
+} usb3_bos_desc = {
+	.bos = {
+		.bLength	 = USB_DT_BOS_SIZE,
+		.bDescriptorType = USB_DT_BOS,
+		.wTotalLength	 = cpu_to_le16(sizeof(usb3_bos_desc)),
+		.bNumDeviceCaps	 = 1
+	},
+	.ss_cap = {
+		.bLength		= USB_DT_USB_SS_CAP_SIZE,
+		.bDescriptorType	= USB_DT_DEVICE_CAPABILITY,
+		.bDevCapabilityType	= USB_SS_CAP_TYPE,
+		.wSpeedSupported	= cpu_to_le16(USB_5GBPS_OPERATION),
+		.bFunctionalitySupport	= ilog2(USB_5GBPS_OPERATION)
+	}
+};
+
+static int get_root_hub_port_number(struct usb_device *dev, u8 *port_number)
+{
+	struct usb_device *first_hub_device = dev;
+
+	if (!dev->parent) {
+		(*port_number) = 0;
+		return -EINVAL;
+	}
+
+	while (first_hub_device->parent->parent)
+		first_hub_device = first_hub_device->parent;
+
+	(*port_number) = first_hub_device->portnum - 1;
+
+	return 0;
+}
+
+static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
+							*mdevs, void *dev_addr)
+{
+	struct rb_node *node = mdevs->usb_devices.rb_node;
+
+	while (node) {
+		struct mausb_usb_device_ctx *usb_device =
+		    rb_entry(node, struct mausb_usb_device_ctx, rb_node);
+
+		if (dev_addr < usb_device->dev_addr)
+			node = usb_device->rb_node.rb_left;
+		else if (dev_addr > usb_device->dev_addr)
+			node = usb_device->rb_node.rb_right;
+		else
+			return usb_device;
+	}
+	return NULL;
+}
+
+static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+	return 0;
+}
+
+static int mausb_hcd_reset(struct usb_hcd *hcd)
+{
+	if (usb_hcd_is_primary_hcd(hcd)) {
+		hcd->speed = HCD_USB2;
+		hcd->self.root_hub->speed = USB_SPEED_HIGH;
+	} else {
+		hcd->speed = HCD_USB3;
+		hcd->self.root_hub->speed = USB_SPEED_SUPER;
+	}
+	hcd->self.no_sg_constraint = 1;
+	hcd->self.sg_tablesize = UINT_MAX;
+
+	return 0;
+}
+
+static int mausb_hcd_start(struct usb_hcd *hcd)
+{
+	hcd->power_budget = 0;
+	hcd->uses_new_polling = 1;
+	return 0;
+}
+
+static void mausb_hcd_stop(struct usb_hcd *hcd)
+{
+	mausb_pr_debug("Not implemented");
+}
+
+static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff)
+{
+	int retval;
+	int changed;
+	int i;
+	struct hub_ctx *hub;
+	unsigned long flags = 0;
+
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	retval  = DIV_ROUND_UP(NUMBER_OF_PORTS + 1, 8);
+	changed = 0;
+
+	memset(buff, 0, (unsigned int)retval);
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		mausb_pr_info("hcd not accessible, hcd speed=%d", hcd->speed);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return 0;
+	}
+
+	for (i = 0; i < NUMBER_OF_PORTS; ++i) {
+		if ((hub->ma_devs[i].port_status & PORT_C_MASK)) {
+			buff[(i + 1) / 8] |= 1 << (i + 1) % 8;
+			changed = 1;
+		}
+	}
+
+	mausb_pr_info("Usb %d.0 : changed=%d, retval=%d",
+		      (hcd->speed == HCD_USB2) ? 2 : 3, changed, retval);
+
+	if (hcd->state == HC_STATE_SUSPENDED && changed == 1) {
+		mausb_pr_info("hcd state is suspended");
+		usb_hcd_resume_root_hub(hcd);
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return changed ? retval : 0;
+}
+
+static int mausb_hcd_bus_resume(struct usb_hcd *hcd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		mausb_pr_info("hcd not accessible, hcd speed=%d", hcd->speed);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return -ESHUTDOWN;
+	}
+	hcd->state = HC_STATE_RUNNING;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static int mausb_hcd_bus_suspend(struct usb_hcd *hcd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	hcd->state = HC_STATE_SUSPENDED;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length)
+{
+	int retval = 0;
+	struct hub_ctx	 *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_hcd *hub_mhcd = hub->mhcd;
+	unsigned long	 flags;
+	bool invalid_rhport = false;
+
+	index = ((__u8)(index & 0x00ff));
+	if (index < 1 || index > NUMBER_OF_PORTS)
+		invalid_rhport = true;
+
+	mausb_pr_info("TypeReq=%d", type_req);
+
+	spin_lock_irqsave(&hub_mhcd->lock, flags);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		mausb_pr_info("hcd not accessible, hcd speed=%d", hcd->speed);
+		spin_unlock_irqrestore(&hub_mhcd->lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	switch (type_req) {
+	case ClearHubFeature:
+		break;
+	case ClearPortFeature:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_clear_port_feature(hcd, type_req, value, index, buff,
+					 length);
+		break;
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		memcpy(buff, &usb3_bos_desc, sizeof(usb3_bos_desc));
+		retval = sizeof(usb3_bos_desc);
+		break;
+	case GetHubDescriptor:
+		mausb_get_hub_descriptor(hcd, type_req, value, index, buff,
+					 length);
+		break;
+	case GetHubStatus:
+		mausb_get_hub_status(hcd, type_req, value, index, buff,
+				     length);
+		break;
+	case GetPortStatus:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_get_port_status(hcd, type_req, value, index, buff,
+				      length);
+		break;
+	case SetHubFeature:
+		retval = -EPIPE;
+		break;
+	case SetPortFeature:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_set_port_feature(hcd, type_req, value, index, buff,
+				       length);
+		break;
+	default:
+		retval = -EPIPE;
+	}
+
+invalid_port:
+	spin_unlock_irqrestore(&hub_mhcd->lock, flags);
+	return retval;
+}
+
+int mausb_probe(struct device *dev)
+{
+	struct mausb_hcd *mausb_hcd;
+	struct usb_hcd	 *hcd_ss;
+	struct usb_hcd	 *hcd_hs;
+	int ret;
+
+	mausb_hcd = dev_get_drvdata(dev);
+	spin_lock_init(&mausb_hcd->lock);
+
+	hcd_hs = usb_create_hcd(&mausb_hc_driver, dev, dev_name(dev));
+	if (!hcd_hs)
+		return -ENOMEM;
+
+	hcd_hs->has_tt = 1;
+	mausb_hcd->hcd_hs_ctx	    = (struct hub_ctx *)hcd_hs->hcd_priv;
+	mausb_hcd->hcd_hs_ctx->mhcd = mausb_hcd;
+	mausb_hcd->hcd_hs_ctx->hcd  = hcd_hs;
+	memset(mausb_hcd->hcd_hs_ctx->ma_devs, 0,
+	       sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
+
+	ret = usb_add_hcd(hcd_hs, 0, 0);
+	if (ret) {
+		mausb_pr_err("usb_add_hcd failed");
+		goto put_hcd_hs;
+	}
+
+	hcd_ss = usb_create_shared_hcd(&mausb_hc_driver, dev, dev_name(dev),
+				       hcd_hs);
+	if (!hcd_ss) {
+		ret = -ENOMEM;
+		goto remove_hcd_hs;
+	}
+	mausb_hcd->hcd_ss_ctx	    = (struct hub_ctx *)hcd_ss->hcd_priv;
+	mausb_hcd->hcd_ss_ctx->mhcd = mausb_hcd;
+	mausb_hcd->hcd_ss_ctx->hcd  = hcd_ss;
+
+	memset(mausb_hcd->hcd_ss_ctx->ma_devs, 0,
+	       sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
+
+	ret = usb_add_hcd(hcd_ss, 0, 0);
+	if (ret) {
+		mausb_pr_err("usb_add_hcd failed");
+		goto put_hcd_ss;
+	}
+
+	return ret;
+
+put_hcd_ss:
+	usb_put_hcd(hcd_ss);
+remove_hcd_hs:
+	usb_remove_hcd(hcd_hs);
+put_hcd_hs:
+	usb_put_hcd(hcd_hs);
+	mausb_hcd->hcd_hs_ctx = NULL;
+	mausb_hcd->hcd_ss_ctx = NULL;
+	return ret;
+}
+
+static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length)
+{
+	u8 width;
+	struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buff;
+
+	memset(desc, 0, sizeof(struct usb_hub_descriptor));
+
+	if (hcd->speed == HCD_USB3) {
+		desc->bDescriptorType	   = USB_DT_SS_HUB;
+		desc->bDescLength	   = 12;
+		desc->wHubCharacteristics  =
+		    cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+		desc->bNbrPorts		   = NUMBER_OF_PORTS;
+		desc->u.ss.bHubHdrDecLat   = 0x04;
+		desc->u.ss.DeviceRemovable = cpu_to_le16(0xffff);
+	} else {
+		desc->bDescriptorType	  = USB_DT_HUB;
+		desc->wHubCharacteristics =
+		    cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+		desc->bNbrPorts		  = NUMBER_OF_PORTS;
+		width			  = (u8)(desc->bNbrPorts / 8 + 1);
+		desc->bDescLength	  = USB_DT_HUB_NONVAR_SIZE + 2 * width;
+
+		memset(&desc->u.hs.DeviceRemovable[0], 0, width);
+		memset(&desc->u.hs.DeviceRemovable[width], 0xff, width);
+	}
+}
+
+static void mausb_set_port_feature(struct usb_hcd *hcd, u16 type_req,
+				   u16 value, u16 index, char *buff,
+				   u16 length)
+{
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	switch (value) {
+	case USB_PORT_FEAT_LINK_STATE:
+		mausb_pr_debug("USB_PORT_FEAT_LINK_STATE");
+		if (hcd->speed == HCD_USB3) {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		} else {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		}
+		break;
+	case USB_PORT_FEAT_U1_TIMEOUT:
+	case USB_PORT_FEAT_U2_TIMEOUT:
+		break;
+	case USB_PORT_FEAT_SUSPEND:
+		break;
+	case USB_PORT_FEAT_POWER:
+		mausb_pr_debug("USB_PORT_FEAT_POWER");
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status |=
+			    USB_SS_PORT_STAT_POWER;
+		} else {
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_POWER;
+		}
+		break;
+	case USB_PORT_FEAT_BH_PORT_RESET:
+		mausb_pr_debug("USB_PORT_FEAT_BH_PORT_RESET");
+	case USB_PORT_FEAT_RESET:
+		mausb_pr_debug("USB_PORT_FEAT_RESET hcd->speed=%d", hcd->speed);
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status = 0;
+			hub->ma_devs[index - 1].port_status =
+			    (USB_SS_PORT_STAT_POWER |
+			     USB_PORT_STAT_CONNECTION | USB_PORT_STAT_RESET);
+		} else if (hub->ma_devs[index - 1].port_status
+			   & USB_PORT_STAT_ENABLE) {
+			hub->ma_devs[index - 1].port_status &=
+			    ~(USB_PORT_STAT_ENABLE |
+			      USB_PORT_STAT_LOW_SPEED |
+			      USB_PORT_STAT_HIGH_SPEED);
+		}
+	default:
+		mausb_pr_info("Default value=%d", value);
+
+		if (hcd->speed == HCD_USB3) {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		} else {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		}
+	}
+}
+
+static void mausb_get_port_status(struct usb_hcd *hcd, u16 type_req,
+				  u16 value, u16 index, char *buff,
+				  u16 length)
+{
+	u8 dev_speed;
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	if ((hub->ma_devs[index - 1].port_status &
+				(1 << USB_PORT_FEAT_RESET)) != 0) {
+		mausb_pr_info("Finished reset hcd->speed=%d", hcd->speed);
+
+		dev_speed = hub->ma_devs[index - 1].dev_speed;
+		switch (dev_speed) {
+		case LOW_SPEED:
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_LOW_SPEED;
+			break;
+		case HIGH_SPEED:
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_HIGH_SPEED;
+			break;
+		default:
+			mausb_pr_info("Not updating port_status for device speed %d",
+				      dev_speed);
+		}
+
+		hub->ma_devs[index - 1].port_status |=
+		    (1 << USB_PORT_FEAT_C_RESET) | USB_PORT_STAT_ENABLE;
+		hub->ma_devs[index - 1].port_status &=
+		    ~(1 << USB_PORT_FEAT_RESET);
+	}
+
+	((__le16 *)buff)[0] = cpu_to_le16(hub->ma_devs[index - 1].port_status);
+	((__le16 *)buff)[1] =
+	    cpu_to_le16(hub->ma_devs[index - 1].port_status >> 16);
+
+	mausb_pr_info("port_status=%d", hub->ma_devs[index - 1].port_status);
+}
+
+static void mausb_clear_port_feature(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length)
+{
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	switch (value) {
+	case USB_PORT_FEAT_SUSPEND:
+		break;
+	case USB_PORT_FEAT_POWER:
+		mausb_pr_debug("USB_PORT_FEAT_POWER");
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status &=
+			    ~USB_SS_PORT_STAT_POWER;
+		} else {
+			hub->ma_devs[index - 1].port_status &=
+			    ~USB_PORT_STAT_POWER;
+		}
+		break;
+	case USB_PORT_FEAT_RESET:
+
+	case USB_PORT_FEAT_C_RESET:
+
+	default:
+		mausb_pr_info("Default value: %d", value);
+
+		hub->ma_devs[index - 1].port_status &= ~(1 << value);
+	}
+}
+
+static void mausb_get_hub_status(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length)
+{
+	*(__le32 *)buff = cpu_to_le32(0);
+}
+
+static int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	mausb_pr_info("Usb device=%p", dev);
+
+	return 1;
+}
+
+static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number;
+	s16	dev_handle;
+	int	status;
+	struct hub_ctx   *hub  = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_dev	    *mdev = NULL;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct mausb_endpoint_ctx   *ep_ctx = dev->ep0.hcpriv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return;
+	}
+
+	mdev  = &hub->ma_devs[port_number];
+
+	usb_device_ctx = mausb_find_usb_device(mdev, dev);
+	if (!usb_device_ctx) {
+		mausb_pr_warn("device_ctx is not found");
+		return;
+	}
+
+	dev_handle = usb_device_ctx->dev_handle;
+
+	if (ep_ctx) {
+		dev->ep0.hcpriv = NULL;
+		kfree(ep_ctx);
+
+	} else {
+		mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle);
+	}
+
+	rb_erase(&usb_device_ctx->rb_node, &mdev->usb_devices);
+	mausb_pr_info("USB device deleted device=%p", usb_device_ctx->dev_addr);
+	kfree(usb_device_ctx);
+}
+
+static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number;
+	int	status;
+	struct hub_ctx	*hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_warn("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx)
+		return -ENODEV;
+
+	mausb_pr_info("dev_handle=%#x, dev_speed=%#x",
+		      usb_device_ctx->dev_handle, dev->speed);
+
+	if (dev->speed >= USB_SPEED_SUPER)
+		mausb_pr_info("USB 3.0");
+	else
+		mausb_pr_info("USB 2.0");
+
+	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED) {
+		status = mausb_enable_device(hcd, dev);
+		if (status < 0)
+			return status;
+	}
+
+	endpoint_ctx = dev->ep0.hcpriv;
+	if (!endpoint_ctx) {
+		mausb_pr_err("endpoint_ctx is NULL: dev_handle=%#x",
+			     usb_device_ctx->dev_handle);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			      struct usb_host_endpoint *endpoint)
+{
+	int	status;
+	u8	port_number;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_dev_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return 0;
+	}
+
+	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!usb_dev_ctx) {
+		mausb_pr_warn("Device not found");
+		return -ENODEV;
+	}
+
+	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
+	if (!endpoint_ctx)
+		return -ENOMEM;
+
+	endpoint_ctx->dev_handle	= usb_dev_ctx->dev_handle;
+	endpoint_ctx->usb_device_ctx	= usb_dev_ctx;
+	endpoint->hcpriv		= endpoint_ctx;
+
+	return 0;
+}
+
+static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			       struct usb_host_endpoint *endpoint)
+{
+	u8	port_number;
+	int	status;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_dev_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx = endpoint->hcpriv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!endpoint_ctx) {
+		mausb_pr_err("Endpoint context doesn't exist");
+		return 0;
+	}
+	if (!usb_dev_ctx) {
+		mausb_pr_err("Usb device context doesn't exist");
+		return -ENODEV;
+	}
+
+	endpoint->hcpriv = NULL;
+	kfree(endpoint_ctx);
+	return 0;
+}
+
+static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	int status;
+	u8 port_number;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx)
+		return -ENODEV;
+
+	mausb_pr_info("Device assigned and addressed usb_device_ctx=%p",
+		      usb_device_ctx);
+
+	return 0;
+}
+
+static int mausb_is_hub_device(struct usb_device *dev)
+{
+	return dev->descriptor.bDeviceClass == 0x09;
+}
+
+static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number = 0;
+	int	status	    = 0;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+
+	if (mausb_is_hub_device(dev)) {
+		mausb_pr_warn("Device is hub");
+		return 0;
+	}
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		mausb_pr_warn("Device not found");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
+				   struct usb_tt *tt, gfp_t mem_flags)
+{
+	int	status;
+	u8	port_number;
+	u16 max_exit_latency = 0;
+	u8  mtt = 0;
+	u8  ttt = 0;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	if (dev->speed == USB_SPEED_HIGH) {
+		mtt = tt->multi == 0 ? 1 : 0;
+		ttt = (u8)tt->think_time;
+	}
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return 0;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
+					       dev);
+
+	if (!usb_device_ctx) {
+		mausb_pr_err("USB device not found");
+		return -ENODEV;
+	}
+
+	if (dev->usb3_lpm_u1_enabled)
+		max_exit_latency = (u16)dev->u1_params.mel;
+	else if (dev->usb3_lpm_u2_enabled)
+		max_exit_latency = (u16)dev->u2_params.mel;
+
+	return 0;
+}
+
+static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	mausb_pr_debug("Not implemented");
+	return 0;
+}
+
+static void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	mausb_pr_debug("Not implemented");
+}
+
+static void mausb_endpoint_disable(struct usb_hcd *hcd,
+				   struct usb_host_endpoint *endpoint)
+{
+	mausb_pr_debug("Not implemented");
+}
+
+static void mausb_endpoint_reset(struct usb_hcd *hcd,
+				 struct usb_host_endpoint *endpoint)
+{
+	int status;
+	int is_control;
+	int epnum;
+	int is_out;
+	u16	dev_handle;
+	u8	tsp;
+	u8	port_number;
+	struct hub_ctx		    *hub;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct usb_device	    *dev;
+	struct mausb_endpoint_ctx   *ep_ctx;
+
+	ep_ctx = endpoint->hcpriv;
+	if (!ep_ctx) {
+		mausb_pr_err("ep->hcpriv is NULL");
+		return;
+	}
+
+	usb_device_ctx	= ep_ctx->usb_device_ctx;
+	dev_handle	= usb_device_ctx->dev_handle;
+	dev		= usb_device_ctx->dev_addr;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return;
+	}
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	is_control = usb_endpoint_xfer_control(&endpoint->desc);
+	epnum = usb_endpoint_num(&endpoint->desc);
+	is_out = usb_endpoint_dir_out(&endpoint->desc);
+	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
+
+	if (status < 0)
+		return;
+
+	if (status != EUCLEAN) {
+		if (!tsp) {
+			usb_settoggle(dev, epnum, is_out, 0U);
+			if (is_control)
+				usb_settoggle(dev, epnum, !is_out, 0U);
+		}
+
+		return;
+	}
+
+	if (tsp)
+		return;
+
+	mausb_pr_info("ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+}
+
+static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	int status;
+	u8  port_number;
+	u16 dev_handle;
+	struct hub_ctx		    *hub;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!usb_device_ctx ||
+	    usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
+		return 0;
+
+	dev_handle = usb_device_ctx->dev_handle;
+
+	return 0;
+}
+
+void mausb_clear_hcd_madev(u16 port_number)
+{
+	unsigned long flags = 0;
+
+	if (port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_err("port_number out of range, port_number=%x",
+			     port_number);
+		return;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	memset(&mhcd->hcd_hs_ctx->ma_devs[port_number], 0,
+	       sizeof(struct mausb_dev));
+	memset(&mhcd->hcd_ss_ctx->ma_devs[port_number], 0,
+	       sizeof(struct mausb_dev));
+
+	mhcd->connected_ports &= ~(1 << port_number);
+
+	mhcd->hcd_hs_ctx->ma_devs[port_number].port_status =
+							USB_PORT_STAT_POWER;
+	mhcd->hcd_ss_ctx->ma_devs[port_number].port_status =
+							USB_SS_PORT_STAT_POWER;
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+}
diff --git a/drivers/usb/mausb_host/hcd.h b/drivers/usb/mausb_host/hcd.h
index cac62ba1f1e2..cbef70a2f985 100644
--- a/drivers/usb/mausb_host/hcd.h
+++ b/drivers/usb/mausb_host/hcd.h
@@ -24,9 +24,6 @@
 
 #define RESPONSE_TIMEOUT	5000
 
-#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
-#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
-
 enum mausb_device_type {
 	USBDEVICE = 0,
 	USB20HUB  = 1,
@@ -68,4 +65,87 @@ struct hub_ctx {
 int mausb_init_hcd(void);
 void mausb_deinit_hcd(void);
 
+#define PORT_C_MASK \
+		((USB_PORT_STAT_C_CONNECTION \
+		| USB_PORT_STAT_C_ENABLE \
+		| USB_PORT_STAT_C_SUSPEND \
+		| USB_PORT_STAT_C_OVERCURRENT \
+		| USB_PORT_STAT_C_RESET) << 16)
+
+#define MAUSB_PORT_20_STATUS_CONNECT         0x0001
+#define MAUSB_PORT_20_STATUS_ENABLE          0x0002
+#define MAUSB_PORT_20_STATUS_SUSPEND         0x0004
+#define MAUSB_PORT_20_STATUS_OVER_CURRENT    0x0008
+#define MAUSB_PORT_20_STATUS_RESET           0x0010
+#define MAUSB_PORT_20_STATUS_POWER           0x0100
+#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
+#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
+
+#define MAUSB_CHANGE_PORT_20_STATUS_CONNECT  0x010000
+#define MAUSB_CHANGE_PORT_20_STATUS_RESET    0x100000
+
+/* USB 3.2 specification chapter 10.16.2.6.1 table 10-13 page 440 */
+#define MAUSB_PORT_30_STATUS_CONNECT              0x0001
+#define MAUSB_PORT_30_STATUS_ENABLE               0x0002
+#define MAUSB_PORT_30_STATUS_OVER_CURRENT         0x0008
+#define MAUSB_PORT_30_STATUS_RESET                0x0010
+#define MAUSB_PORT_30_LINK_STATE_U0               0x0000
+#define MAUSB_PORT_30_LINK_STATE_U1               0x0020
+#define MAUSB_PORT_30_LINK_STATE_U2               0x0040
+#define MAUSB_PORT_30_LINK_STATE_U3               0x0060
+#define MAUSB_PORT_30_LINK_STATE_DISABLED         0x0080
+#define MAUSB_PORT_30_LINK_STATE_RX_DETECT        0x00A0
+#define MAUSB_PORT_30_LINK_STATE_INACTIVE         0x00C0
+#define MAUSB_PORT_30_LINK_STATE_POLLING          0x00E0
+#define MAUSB_PORT_30_LINK_STATE_RECOVERY         0x0100
+#define MAUSB_PORT_30_LINK_STATE_HOT_RESET        0x0120
+#define MAUSB_PORT_30_LINK_STATE_COMPLIANCE_MODE  0x0140
+#define MAUSB_PORT_30_LINK_STATE_LOOPBACK         0x0160
+#define MAUSB_PORT_30_STATUS_POWER                0x0200
+#define MAUSB_PORT_30_STATUS_SUPER_SPEED          0x0400
+#define MAUSB_PORT_30_CLEAR_LINK_STATE            0xFE1F
+
+/* USB 3.2 specification chapter 10.16.2.6.2 table 10-14 page 443 */
+#define MAUSB_CHANGE_PORT_30_STATUS_CONNECT              0x010000
+#define MAUSB_CHANGE_PORT_30_STATUS_OVER_CURRENT         0x080000
+#define MAUSB_CHANGE_PORT_30_STATUS_RESET                0x100000
+#define MAUSB_CHANGE_PORT_30_BH_STATUS_RESET             0x200000
+#define MAUSB_CHANGE_PORT_30_LINK_STATE                  0x400000
+#define MAUSB_CHANGE_PORT_30_CONFIG_ERROR                0x800000
+
+/* USB 3.2 specification chapter 10.16.2.4 table 10-10 page 438 */
+#define MAUSB_HUB_30_POWER_GOOD              0x00
+#define MAUSB_HUB_30_LOCAL_POWER_SOURCE_LOST 0x01
+#define MAUSB_HUB_30_OVER_CURRENT            0x02
+
+/* USB 3.2 specification chapter 10.16.2.4 table 10-11 page 438 */
+#define MAUSB_CHANGE_HUB_30_LOCAL_POWER_SOURCE_LOST  0x10000
+#define MAUSB_CHANGE_HUB_30_OVER_CURRENT             0x20000
+
+#define DEV_HANDLE_NOT_ASSIGNED	-1
+
+struct mausb_usb_device_ctx {
+	s32		dev_handle;
+	bool		addressed;
+	void		*dev_addr;
+	struct rb_node	rb_node;
+};
+
+struct mausb_endpoint_ctx {
+	u16	ep_handle;
+	u16	dev_handle;
+	void	*ma_dev;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+};
+
+struct mausb_urb_ctx {
+	struct urb		*urb;
+	struct rb_node		rb_node;
+	struct work_struct	work;
+};
+
+int mausb_probe(struct device *dev);
+
+void mausb_clear_hcd_madev(u16 port_number);
+
 #endif /* __MAUSB_HCD_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (3 preceding siblings ...)
  2020-04-25  9:19     ` [PATCH v5 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-26  0:32       ` Alan Stern
  2020-04-25  9:19     ` [PATCH v5 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication vladimir.stankovic
                       ` (4 subsequent siblings)
  9 siblings, 1 reply; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Protocol adaptation layer (PAL) implementation has been added to
introduce MA-USB structures and logic.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile        |    1 +
 drivers/usb/mausb_host/hcd.c           |  523 ++++++++++-
 drivers/usb/mausb_host/hcd.h           |   11 +
 drivers/usb/mausb_host/hpal.c          | 1094 ++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal.h          |  289 +++++++
 drivers/usb/mausb_host/ma_usb.h        |  869 +++++++++++++++++++
 drivers/usb/mausb_host/mausb_address.h |   26 +
 drivers/usb/mausb_host/mausb_core.c    |   13 +-
 drivers/usb/mausb_host/mausb_event.h   |  224 +++++
 9 files changed, 3040 insertions(+), 10 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal.c
 create mode 100644 drivers/usb/mausb_host/hpal.h
 create mode 100644 drivers/usb/mausb_host/ma_usb.h
 create mode 100644 drivers/usb/mausb_host/mausb_address.h
 create mode 100644 drivers/usb/mausb_host/mausb_event.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index cce4696682b2..829314b15cbb 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -10,5 +10,6 @@ mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
 mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
+mausb_host-y += hpal.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
index d8ede5b33fb4..5967c30d49a3 100644
--- a/drivers/usb/mausb_host/hcd.c
+++ b/drivers/usb/mausb_host/hcd.c
@@ -9,6 +9,7 @@
 #include <linux/limits.h>
 #include <linux/module.h>
 
+#include "hpal.h"
 #include "utils.h"
 
 static int mausb_open(struct inode *inode, struct file *file);
@@ -195,6 +196,90 @@ void mausb_deinit_hcd(void)
 	}
 }
 
+void mausb_port_has_changed(const enum mausb_device_type device_type,
+			    const enum mausb_device_speed device_speed,
+			    void *ma_dev)
+{
+	struct usb_hcd *hcd;
+	unsigned long flags = 0;
+	struct mausb_device *dev = ma_dev;
+	u16 port_number = dev->port_number;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+		    USB_PORT_STAT_CONNECTION | (1 <<
+						USB_PORT_FEAT_C_CONNECTION);
+
+		if (device_speed == LOW_SPEED) {
+			mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			    MAUSB_PORT_20_STATUS_LOW_SPEED;
+			mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
+			    LOW_SPEED;
+		} else if (device_speed == HIGH_SPEED) {
+			mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			    MAUSB_PORT_20_STATUS_HIGH_SPEED;
+			mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
+			    HIGH_SPEED;
+		}
+
+		hcd = mhcd->hcd_hs_ctx->hcd;
+		mhcd->hcd_hs_ctx->ma_devs[port_number].ma_dev = ma_dev;
+	} else {
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
+		    USB_PORT_STAT_CONNECTION | (1 <<
+						USB_PORT_FEAT_C_CONNECTION);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].dev_speed = SUPER_SPEED;
+
+		hcd = mhcd->hcd_ss_ctx->hcd;
+		mhcd->hcd_ss_ctx->ma_devs[port_number].ma_dev = ma_dev;
+	}
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	usb_hcd_poll_rh_status(hcd);
+}
+
+void mausb_hcd_disconnect(const u16 port_number,
+			  const enum mausb_device_type device_type,
+			  const enum mausb_device_speed device_speed)
+{
+	struct usb_hcd *hcd;
+	unsigned long flags = 0;
+
+	if (port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_err("port number out of range, port_number=%x",
+			     port_number);
+		return;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_CONNECTION);
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_ENABLE);
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			(1 << USB_PORT_FEAT_C_CONNECTION);
+		hcd = mhcd->hcd_hs_ctx->hcd;
+	} else {
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_CONNECTION);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_ENABLE);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
+			(1 << USB_PORT_FEAT_C_CONNECTION);
+		hcd = mhcd->hcd_ss_ctx->hcd;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+	if (!hcd)
+		return;
+
+	usb_hcd_poll_rh_status(hcd);
+}
+
 static const char driver_name[] = "MA-USB host controller";
 
 static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
@@ -235,12 +320,31 @@ static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff);
 static int mausb_hcd_reset(struct usb_hcd *hcd);
 static int mausb_hcd_start(struct usb_hcd *hcd);
 static void mausb_hcd_stop(struct usb_hcd *hcd);
+static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				 int status);
+static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				 gfp_t mem_flags);
 static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 				   struct usb_tt *tt, gfp_t mem_flags);
 static void mausb_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *dev);
 static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
 static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
 
+static void mausb_print_urb(struct urb *request)
+{
+	mausb_pr_debug("URB: urb=%p, ep_handle=%#x, packet_num=%d, setup_dma=%lld, is_setup_packet=%d, is_ep=%d, is_sg=%d, num_sgs=%d, num_mapped_sgs=%d, status=%d, is_transfer_buffer=%d, transfer_buffer_length=%d, is_transfer_dma=%llu, transfer_flags=%d, is_hcpriv=%d",
+		       request, ((struct mausb_endpoint_ctx *)
+				 request->ep->hcpriv)->ep_handle,
+		       request->number_of_packets, request->setup_dma,
+		       request->setup_packet ? 1 : 0, request->ep ? 1 : 0,
+		       request->sg ? 1 : 0, request->num_sgs,
+		       request->num_mapped_sgs, request->status,
+		       request->transfer_buffer ? 1 : 0,
+		       request->transfer_buffer_length,
+		       request->transfer_dma, request->transfer_flags,
+		       (request->ep && request->ep->hcpriv) ? 1 : 0);
+}
+
 static const struct hc_driver mausb_hc_driver = {
 	.description  =  driver_name,
 	.product_desc = driver_name,
@@ -252,6 +356,9 @@ static const struct hc_driver mausb_hc_driver = {
 	.start = mausb_hcd_start,
 	.stop  = mausb_hcd_stop,
 
+	.urb_enqueue = mausb_hcd_urb_enqueue,
+	.urb_dequeue = mausb_hcd_urb_dequeue,
+
 	.get_frame_number = mausb_hcd_get_frame_number,
 
 	.hub_status_data   = mausb_hcd_hub_status,
@@ -311,6 +418,25 @@ static int get_root_hub_port_number(struct usb_device *dev, u8 *port_number)
 	return 0;
 }
 
+static int usb_to_mausb_device_speed(u8 speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return MA_USB_SPEED_LOW_SPEED;
+	case USB_SPEED_FULL:
+		return MA_USB_SPEED_FULL_SPEED;
+	case USB_SPEED_WIRELESS:
+	case USB_SPEED_HIGH:
+		return MA_USB_SPEED_HIGH_SPEED;
+	case USB_SPEED_SUPER:
+		return MA_USB_SPEED_SUPER_SPEED;
+	case USB_SPEED_SUPER_PLUS:
+		return MA_USB_SPEED_SUPER_SPEED_PLUS;
+	default:
+		return -EINVAL;
+	}
+}
+
 static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
 							*mdevs, void *dev_addr)
 {
@@ -330,6 +456,31 @@ static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
 	return NULL;
 }
 
+static int mausb_insert_usb_device(struct mausb_dev *mdevs,
+				   struct mausb_usb_device_ctx *usb_device)
+{
+	struct rb_node **new_node = &mdevs->usb_devices.rb_node;
+	struct rb_node *parent = NULL;
+	struct mausb_usb_device_ctx *current_usb_device = NULL;
+
+	while (*new_node) {
+		parent = *new_node;
+		current_usb_device = rb_entry(*new_node,
+					      struct mausb_usb_device_ctx,
+					      rb_node);
+
+		if (usb_device->dev_addr < current_usb_device->dev_addr)
+			new_node = &((*new_node)->rb_left);
+		else if (usb_device->dev_addr > current_usb_device->dev_addr)
+			new_node = &((*new_node)->rb_right);
+		else
+			return -EEXIST;
+	}
+	rb_link_node(&usb_device->rb_node, parent, new_node);
+	rb_insert_color(&usb_device->rb_node, &mdevs->usb_devices);
+	return 0;
+}
+
 static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
 {
 	return 0;
@@ -504,6 +655,123 @@ static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
 	return retval;
 }
 
+static int mausb_validate_urb(struct urb *urb)
+{
+	if (!urb) {
+		mausb_pr_err("urb is NULL");
+		return -EINVAL;
+	}
+
+	if (!urb->ep->hcpriv) {
+		mausb_pr_err("urb->ep->hcpriv is NULL");
+		return -EINVAL;
+	}
+
+	if (!urb->ep->enabled) {
+		mausb_pr_err("Endpoint not enabled");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				 gfp_t mem_flags)
+{
+	struct mausb_endpoint_ctx *endpoint_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_urb_ctx	  *urb_ctx;
+	int status = 0;
+
+	if (mausb_validate_urb(urb) < 0) {
+		mausb_pr_err("Hpal urb enqueue failed");
+		return -EPROTO;
+	}
+
+	endpoint_ctx = urb->ep->hcpriv;
+	ma_dev = endpoint_ctx->ma_dev;
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - finish urb immediately");
+		return -EHOSTDOWN;
+	}
+
+	urb->hcpriv = hcd;
+
+	mausb_pr_debug("ep_handle=%#x, dev_handle=%#x, urb_reject=%d",
+		       endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		       atomic_read(&urb->reject));
+
+	status = mausb_insert_urb_in_tree(urb, true);
+	if (status) {
+		mausb_pr_err("Hpal urb enqueue failed");
+		return status;
+	}
+
+	atomic_inc(&urb->use_count);
+
+	mausb_print_urb(urb);
+
+	/*
+	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
+	 * should not, so it is breaking the USB drive on the linux
+	 */
+	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
+
+	status = mausb_data_req_enqueue_event(ma_dev, endpoint_ctx->ep_handle,
+					      urb);
+	if (status < 0) {
+		urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+		atomic_dec(&urb->use_count);
+		if (urb_ctx) {
+			mausb_uninit_data_iterator(&urb_ctx->iterator);
+			kfree(urb_ctx);
+		}
+	}
+
+	return status;
+}
+
+static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				 int status)
+{
+	struct mausb_endpoint_ctx *endpoint_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_urb_ctx	  *urb_ctx;
+
+	mausb_pr_info("Urb=%p", urb);
+
+	urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+	if (!urb_ctx) {
+		mausb_pr_warn("Urb=%p is not in tree", urb);
+		return 0;
+	}
+
+	endpoint_ctx = urb->ep->hcpriv;
+	ma_dev	     = endpoint_ctx->ma_dev;
+
+	queue_work(ma_dev->workq, &urb_ctx->work);
+
+	return 0;
+}
+
+void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status)
+{
+	struct mausb_urb_ctx *urb_ctx =
+		mausb_unlink_and_delete_urb_from_tree(urb, status);
+
+	if (urb_ctx) {
+		mausb_uninit_data_iterator(&urb_ctx->iterator);
+		kfree(urb_ctx);
+
+		urb->status	   = status;
+		urb->actual_length = actual_length;
+
+		atomic_dec(&urb->use_count);
+		usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+		return;
+	}
+}
+
 int mausb_probe(struct device *dev)
 {
 	struct mausb_hcd *mausb_hcd;
@@ -763,8 +1031,10 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 	u8	port_number;
 	s16	dev_handle;
 	int	status;
+	unsigned long	 flags;
 	struct hub_ctx   *hub  = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_dev	    *mdev = NULL;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct mausb_endpoint_ctx   *ep_ctx = dev->ep0.hcpriv;
 
@@ -777,6 +1047,16 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 
 	mdev  = &hub->ma_devs[port_number];
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(mdev, dev);
 	if (!usb_device_ctx) {
 		mausb_pr_warn("device_ctx is not found");
@@ -785,6 +1065,13 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 
 	dev_handle = usb_device_ctx->dev_handle;
 
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - free usbdevice immediately");
+		dev->ep0.hcpriv = NULL;
+		kfree(ep_ctx);
+		goto free_dev;
+	}
+
 	if (ep_ctx) {
 		dev->ep0.hcpriv = NULL;
 		kfree(ep_ctx);
@@ -793,16 +1080,60 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle);
 	}
 
+free_dev:
+	if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
+		mausb_pr_info("All usb devices destroyed - proceed with disconnecting");
+		queue_work(ma_dev->workq, &ma_dev->socket_disconnect_work);
+	}
+
 	rb_erase(&usb_device_ctx->rb_node, &mdev->usb_devices);
 	mausb_pr_info("USB device deleted device=%p", usb_device_ctx->dev_addr);
 	kfree(usb_device_ctx);
+
+	if (kref_put(&ma_dev->refcount, mausb_release_ma_dev_async))
+		mausb_clear_hcd_madev(port_number);
+}
+
+static struct mausb_usb_device_ctx *
+mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
+		       struct mausb_device *ma_dev, u16 port_number,
+		       int *status)
+{
+	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+
+	usb_device_ctx = kzalloc(sizeof(*usb_device_ctx), GFP_ATOMIC);
+	if (!usb_device_ctx) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	usb_device_ctx->dev_addr   = dev;
+	usb_device_ctx->dev_handle = DEV_HANDLE_NOT_ASSIGNED;
+	usb_device_ctx->addressed  = false;
+
+	if (mausb_insert_usb_device(&hub->ma_devs[port_number],
+				    usb_device_ctx)) {
+		mausb_pr_warn("device_ctx already exists");
+		kfree(usb_device_ctx);
+		*status = -EEXIST;
+		return NULL;
+	}
+
+	kref_get(&ma_dev->refcount);
+	mausb_pr_info("New USB device added device=%p",
+		      usb_device_ctx->dev_addr);
+	atomic_inc(&ma_dev->num_of_usb_devices);
+
+	return usb_device_ctx;
 }
 
 static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 {
 	u8	port_number;
 	int	status;
+	unsigned long	flags;
 	struct hub_ctx	*hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx;
 
@@ -813,9 +1144,23 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
-	if (!usb_device_ctx)
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_warn("MAUSB device not found on port_number=%d",
+			      port_number);
 		return -ENODEV;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
+							port_number, &status);
+		if (!usb_device_ctx)
+			return status;
+	}
 
 	mausb_pr_info("dev_handle=%#x, dev_speed=%#x",
 		      usb_device_ctx->dev_handle, dev->speed);
@@ -846,9 +1191,13 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	int	status;
 	u8	port_number;
+	struct ma_usb_ephandlereq_desc_ss  descriptor_ss;
+	struct ma_usb_ephandlereq_desc_std descriptor;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx;
+	unsigned long flags = 0;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -857,6 +1206,16 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return 0;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!usb_dev_ctx) {
@@ -870,8 +1229,17 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 
 	endpoint_ctx->dev_handle	= usb_dev_ctx->dev_handle;
 	endpoint_ctx->usb_device_ctx	= usb_dev_ctx;
+	endpoint_ctx->ma_dev		= ma_dev;
 	endpoint->hcpriv		= endpoint_ctx;
 
+	if (dev->speed >= USB_SPEED_SUPER) {
+		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
+						    &endpoint->desc,
+						    &endpoint->ss_ep_comp);
+	} else {
+		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+	}
+
 	return 0;
 }
 
@@ -881,8 +1249,10 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 	u8	port_number;
 	int	status;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx = endpoint->hcpriv;
+	unsigned long flags = 0;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -891,6 +1261,16 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!endpoint_ctx) {
@@ -907,12 +1287,78 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 	return 0;
 }
 
+static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
+					  struct usb_device *dev,
+					  struct hub_ctx *hub,
+					  struct mausb_device *ma_dev,
+					  struct mausb_usb_device_ctx
+					  *usb_device_ctx)
+{
+	u8 port_number;
+	int status;
+	int dev_speed;
+	u16 hub_dev_handle		= 0;
+	u16 parent_hs_hub_dev_handle	= 0;
+	u16 parent_hs_hub_port		= 0;
+	struct usb_device		   *first_hub_device = dev;
+	struct mausb_usb_device_ctx	   *hub_device_ctx;
+	struct mausb_endpoint_ctx	   *endpoint_ctx;
+	struct ma_usb_ephandlereq_desc_std descriptor;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		mausb_pr_info("port_number out of range, port_number=%x",
+			      port_number);
+		return -EINVAL;
+	}
+
+	while (first_hub_device->parent->parent)
+		first_hub_device = first_hub_device->parent;
+
+	hub_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
+					       first_hub_device);
+	if (hub_device_ctx)
+		hub_dev_handle = hub_device_ctx->dev_handle;
+
+	if ((dev->speed == USB_SPEED_LOW || dev->speed == USB_SPEED_FULL) &&
+	    first_hub_device->speed == USB_SPEED_HIGH) {
+		parent_hs_hub_dev_handle =
+			mausb_find_usb_device(&hub->ma_devs[port_number],
+					      dev->parent)->dev_handle;
+		parent_hs_hub_port = dev->parent->portnum;
+	}
+
+	dev_speed = usb_to_mausb_device_speed(dev->speed);
+	mausb_pr_info("start... mausb_devspeed=%d, route=%#x, port_number=%d",
+		      dev_speed, dev->route, port_number);
+
+	if (dev_speed == -EINVAL) {
+		mausb_pr_err("bad dev_speed");
+		return -EINVAL;
+	}
+
+	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
+	if (!endpoint_ctx)
+		return -ENOMEM;
+
+	endpoint_ctx->dev_handle     = usb_device_ctx->dev_handle;
+	endpoint_ctx->ma_dev	     = ma_dev;
+	endpoint_ctx->usb_device_ctx = usb_device_ctx;
+	dev->ep0.hcpriv		     = endpoint_ctx;
+
+	mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
+
+	return 0;
+}
+
 static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 {
 	int status;
 	u8 port_number;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
+	unsigned long flags;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -921,13 +1367,27 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
-	if (!usb_device_ctx)
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
 		return -ENODEV;
+	}
 
-	mausb_pr_info("Device assigned and addressed usb_device_ctx=%p",
-		      usb_device_ctx);
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
+							port_number, &status);
+		if (!usb_device_ctx)
+			return status;
+	}
 
+	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
+		return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
+						      usb_device_ctx);
 	return 0;
 }
 
@@ -941,7 +1401,9 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 	u8	port_number = 0;
 	int	status	    = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev = NULL;
 	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+	unsigned long flags = 0;
 
 	if (mausb_is_hub_device(dev)) {
 		mausb_pr_warn("Device is hub");
@@ -955,6 +1417,16 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 	if (!usb_device_ctx) {
 		mausb_pr_warn("Device not found");
@@ -969,10 +1441,12 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	int	status;
 	u8	port_number;
+	unsigned long flags;
 	u16 max_exit_latency = 0;
 	u8  mtt = 0;
 	u8  ttt = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 
 	if (dev->speed == USB_SPEED_HIGH) {
@@ -987,6 +1461,16 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 		return 0;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
 					       dev);
 
@@ -1027,10 +1511,12 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	int is_control;
 	int epnum;
 	int is_out;
+	unsigned long flags;
 	u16	dev_handle;
 	u8	tsp;
 	u8	port_number;
 	struct hub_ctx		    *hub;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct usb_device	    *dev;
 	struct mausb_endpoint_ctx   *ep_ctx;
@@ -1053,14 +1539,21 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	}
 	hub = (struct hub_ctx *)hcd->hcd_priv;
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return;
+	}
+
 	is_control = usb_endpoint_xfer_control(&endpoint->desc);
 	epnum = usb_endpoint_num(&endpoint->desc);
 	is_out = usb_endpoint_dir_out(&endpoint->desc);
 	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
 
-	if (status < 0)
-		return;
-
 	if (status != EUCLEAN) {
 		if (!tsp) {
 			usb_settoggle(dev, epnum, is_out, 0U);
@@ -1083,7 +1576,9 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 	int status;
 	u8  port_number;
 	u16 dev_handle;
+	unsigned long flags;
 	struct hub_ctx		    *hub;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 
 	hub = (struct hub_ctx *)hcd->hcd_priv;
@@ -1095,6 +1590,16 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		mausb_pr_err("MAUSB device not found on port_number=%d",
+			     port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!usb_device_ctx ||
diff --git a/drivers/usb/mausb_host/hcd.h b/drivers/usb/mausb_host/hcd.h
index cbef70a2f985..d4e9267503d1 100644
--- a/drivers/usb/mausb_host/hcd.h
+++ b/drivers/usb/mausb_host/hcd.h
@@ -15,6 +15,8 @@
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 
+#include "hpal.h"
+
 #define DEVICE_NAME "mausb_host_hcd_dev"
 #define CLASS_NAME "mausb"
 
@@ -65,6 +67,13 @@ struct hub_ctx {
 int mausb_init_hcd(void);
 void mausb_deinit_hcd(void);
 
+void mausb_port_has_changed(const enum mausb_device_type device_type,
+			    const enum mausb_device_speed device_speed,
+			    void *ma_dev);
+void mausb_hcd_disconnect(const u16 port_number,
+			  const enum mausb_device_type device_type,
+			  const enum mausb_device_speed device_speed);
+
 #define PORT_C_MASK \
 		((USB_PORT_STAT_C_CONNECTION \
 		| USB_PORT_STAT_C_ENABLE \
@@ -140,11 +149,13 @@ struct mausb_endpoint_ctx {
 
 struct mausb_urb_ctx {
 	struct urb		*urb;
+	struct mausb_data_iter	iterator;
 	struct rb_node		rb_node;
 	struct work_struct	work;
 };
 
 int mausb_probe(struct device *dev);
+void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status);
 
 void mausb_clear_hcd_madev(u16 port_number);
 
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
new file mode 100644
index 000000000000..e10b98002afd
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal.c
@@ -0,0 +1,1094 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal.h"
+
+#include <linux/circ_buf.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/uio.h>
+
+#include "hcd.h"
+#include "utils.h"
+
+struct mss mss;
+
+static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
+				void *data);
+static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
+static int mausb_start_heartbeat_timer(void);
+
+static inline struct mausb_urb_ctx *__mausb_find_urb_in_tree(struct urb *urb)
+{
+	struct rb_node *node = mhcd->mausb_urbs.rb_node;
+
+	while (node) {
+		struct mausb_urb_ctx *urb_ctx =
+		    rb_entry(node, struct mausb_urb_ctx, rb_node);
+
+		if (urb < urb_ctx->urb)
+			node = urb_ctx->rb_node.rb_left;
+		else if (urb > urb_ctx->urb)
+			node = urb_ctx->rb_node.rb_right;
+		else
+			return urb_ctx;
+	}
+	return NULL;
+}
+
+struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb)
+{
+	unsigned long flags = 0;
+	struct mausb_urb_ctx *urb_ctx;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	urb_ctx =  __mausb_find_urb_in_tree(urb);
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return urb_ctx;
+}
+
+static int mausb_insert_urb_ctx_in_tree(struct mausb_urb_ctx *urb_ctx)
+{
+	struct rb_node **new_node = &mhcd->mausb_urbs.rb_node;
+	struct rb_node *parent = NULL;
+	struct mausb_urb_ctx *current_urb = NULL;
+
+	while (*new_node) {
+		parent = *new_node;
+		current_urb = rb_entry(*new_node, struct mausb_urb_ctx,
+				       rb_node);
+
+		if (urb_ctx->urb < current_urb->urb)
+			new_node = &((*new_node)->rb_left);
+		else if (urb_ctx->urb > current_urb->urb)
+			new_node = &((*new_node)->rb_right);
+		else
+			return -EEXIST;
+	}
+	rb_link_node(&urb_ctx->rb_node, parent, new_node);
+	rb_insert_color(&urb_ctx->rb_node, &mhcd->mausb_urbs);
+	return 0;
+}
+
+static void mausb_delete_urb_ctx_from_tree(struct mausb_urb_ctx *urb_ctx)
+{
+	rb_erase(&urb_ctx->rb_node, &mhcd->mausb_urbs);
+}
+
+static struct mausb_urb_ctx *mausb_create_urb_ctx(struct urb *urb, int *status)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+
+	if (!urb) {
+		mausb_pr_err("Urb is NULL");
+		*status = -EINVAL;
+		return NULL;
+	}
+
+	urb_ctx = kzalloc(sizeof(*urb_ctx), GFP_ATOMIC);
+	if (!urb_ctx) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	urb_ctx->urb = urb;
+	INIT_WORK(&urb_ctx->work, mausb_execute_urb_dequeue);
+
+	return urb_ctx;
+}
+
+int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep)
+{
+	unsigned long flags;
+	int status = 0;
+
+	struct mausb_urb_ctx *urb_ctx = mausb_create_urb_ctx(urb, &status);
+
+	if (!urb_ctx)
+		return status;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (link_urb_to_ep) {
+		status = usb_hcd_link_urb_to_ep(urb->hcpriv, urb);
+		if (status) {
+			spin_unlock_irqrestore(&mhcd->lock, flags);
+			mausb_pr_err("Error %d while linking urb to hcd_endpoint",
+				     status);
+			kfree(urb_ctx);
+			return status;
+		}
+	}
+
+	if (mausb_insert_urb_ctx_in_tree(urb_ctx)) {
+		kfree(urb_ctx);
+		if (link_urb_to_ep)
+			usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		mausb_pr_err("Urb_ctx insertion failed");
+		return -EEXIST;
+	}
+
+	mausb_init_data_iterator(&urb_ctx->iterator, urb->transfer_buffer,
+				 urb->transfer_buffer_length, urb->sg,
+				 (unsigned int)urb->num_sgs,
+				 usb_urb_dir_in(urb));
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx,
+					 bool link_urb_to_ep)
+{
+	unsigned long flags;
+	int status;
+
+	if (!urb_ctx)
+		return false;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (link_urb_to_ep) {
+		status = usb_hcd_link_urb_to_ep(urb_ctx->urb->hcpriv,
+						urb_ctx->urb);
+		if (status) {
+			spin_unlock_irqrestore(&mhcd->lock, flags);
+			mausb_pr_err("Error %d while linking urb to hcd_endpoint",
+				     status);
+			return false;
+		}
+	}
+
+	if (mausb_insert_urb_ctx_in_tree(urb_ctx)) {
+		if (link_urb_to_ep)
+			usb_hcd_unlink_urb_from_ep(urb_ctx->urb->hcpriv,
+						   urb_ctx->urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		mausb_pr_err("Urb_ctx insertion failed");
+		return false;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return true;
+}
+
+/*After this function call only valid thing to do with urb is to give it back*/
+struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
+							    int status)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+	unsigned long flags;
+	int ret;
+
+	if (!urb) {
+		mausb_pr_warn("Urb is NULL");
+		return NULL;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	urb_ctx = __mausb_find_urb_in_tree(urb);
+
+	if (!urb_ctx) {
+		mausb_pr_warn("Urb=%p not in tree", urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return NULL;
+	}
+
+	ret = usb_hcd_check_unlink_urb(urb->hcpriv, urb, status);
+
+	if (ret == -EIDRM)
+		mausb_pr_warn("Urb=%p is already unlinked", urb);
+	else
+		usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb);
+
+	mausb_delete_urb_ctx_from_tree(urb_ctx);
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	mausb_pr_debug("Urb=%p is removed from tree", urb);
+
+	return urb_ctx;
+}
+
+void mausb_release_event_resources(struct mausb_event *event)
+{
+	struct ma_usb_hdr_common *receive_buffer = (struct ma_usb_hdr_common *)
+						    event->data.recv_buf;
+
+	kfree(receive_buffer);
+}
+
+void mausb_complete_urb(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size,
+		       event->data.rem_transfer_size, event->status);
+	mausb_complete_request(urb,
+			       event->data.transfer_size -
+			       event->data.rem_transfer_size,
+			       event->status);
+}
+
+static int mausb_get_first_free_port_number(u16 *port_number)
+{
+	(*port_number) = 0;
+	while ((mhcd->connected_ports & (1 << *port_number)) != 0 &&
+	       *port_number < NUMBER_OF_PORTS)
+		++(*port_number);
+
+	if (*port_number == NUMBER_OF_PORTS)
+		return -EINVAL;
+
+	mhcd->connected_ports |= (1 << *port_number);
+
+	return 0;
+}
+
+static inline void mausb_port_has_changed_event(struct mausb_device *dev,
+						struct mausb_event *event)
+{
+	int status;
+	u16 port_number;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	status = mausb_get_first_free_port_number(&port_number);
+	if (status < 0) {
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		mausb_pr_err("There is no free port, schedule delete ma_dev");
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	dev->dev_type	   = event->port_changed.dev_type;
+	dev->dev_speed	   = event->port_changed.dev_speed;
+	dev->lse	   = event->port_changed.lse;
+	dev->dev_connected = 1;
+	dev->port_number   = port_number;
+
+	mausb_port_has_changed(event->port_changed.dev_type,
+			       event->port_changed.dev_speed, dev);
+
+	if ((enum mausb_device_type)event->port_changed.dev_type == USB30HUB)
+		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
+}
+
+static void mausb_heartbeat_timer_func(struct timer_list *timer)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	if (mausb_start_heartbeat_timer() < 0) {
+		mausb_pr_err("Devices disconnecting - app is unresponsive");
+		spin_lock_irqsave(&mss.lock, flags);
+
+		/* Reset connected clients */
+		mss.client_connected = false;
+		mss.missed_heartbeats = 0;
+
+		list_for_each_entry(dev, &mss.madev_list, list_entry) {
+			mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x",
+				       dev->madev_addr);
+			queue_work(dev->workq, &dev->heartbeat_work);
+		}
+
+		complete(&mss.client_stopped);
+		spin_unlock_irqrestore(&mss.lock, flags);
+	}
+}
+
+void mausb_release_ma_dev_async(struct kref *kref)
+{
+	struct mausb_device *dev = container_of(kref, struct mausb_device,
+						refcount);
+
+	mausb_pr_info("Scheduling work for MAUSB device to be deleted");
+
+	schedule_work(&dev->madev_delete_work);
+}
+
+int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
+				  u16 num_of_completed)
+{
+	unsigned long flags;
+	struct mausb_device *dev;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(madev_addr);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -EINVAL;
+	}
+
+	spin_lock(&dev->num_of_user_events_lock);
+	dev->num_of_user_events += num_of_events;
+	dev->num_of_completed_events += num_of_completed;
+	spin_unlock(&dev->num_of_user_events_lock);
+	queue_work(dev->workq, &dev->work);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+}
+
+int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
+				 struct urb *request)
+{
+	struct mausb_event mausb_event;
+
+	mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
+	mausb_event.status = 0;
+
+	mausb_event.data.transfer_type =
+		mausb_transfer_type_from_usb(&request->ep->desc);
+	mausb_event.data.device_id	= dev->id;
+	mausb_event.data.ep_handle	= ep_handle;
+	mausb_event.data.urb		= (uintptr_t)request;
+	mausb_event.data.setup_packet	=
+		(usb_endpoint_xfer_control(&request->ep->desc) &&
+			request->setup_packet);
+	mausb_event.data.transfer_size	= request->transfer_buffer_length;
+	mausb_event.data.direction	= (usb_urb_dir_in(request) ?
+						MAUSB_DATA_MSG_DIRECTION_IN :
+						MAUSB_DATA_MSG_DIRECTION_OUT);
+	mausb_event.data.transfer_size +=
+		((mausb_event.data.direction == MAUSB_DATA_MSG_DIRECTION_OUT &&
+			mausb_event.data.setup_packet) ?
+				MAUSB_CONTROL_SETUP_SIZE : 0);
+	mausb_event.data.rem_transfer_size = mausb_event.data.transfer_size;
+	mausb_event.data.transfer_flags	   = request->transfer_flags;
+	mausb_event.data.transfer_eot	   = false;
+	mausb_event.data.isoch_seg_num	   = (u32)request->number_of_packets;
+	mausb_event.data.recv_buf	   = 0;
+	mausb_event.data.payload_size	   =
+		(usb_endpoint_xfer_isoc(&request->ep->desc) &&
+		 usb_endpoint_dir_out(&request->ep->desc)) ?
+		(request->iso_frame_desc[request->number_of_packets - 1]
+								.offset +
+		 request->iso_frame_desc[request->number_of_packets - 1]
+								.length) : 0;
+
+	if (mausb_event.data.setup_packet) {
+		memcpy(mausb_event.data.hdr_ack, request->setup_packet,
+		       MAUSB_CONTROL_SETUP_SIZE);
+		memcpy(shift_ptr(mausb_event.data.hdr_ack,
+				 MAUSB_CONTROL_SETUP_SIZE),
+		       &request->dev->route, sizeof(request->dev->route));
+	}
+
+	return 0;
+}
+
+void mausb_complete_request(struct urb *urb, u32 actual_length, int status)
+{
+	mausb_hcd_urb_complete(urb, actual_length, status);
+}
+
+int mausb_signal_event(struct mausb_device *dev,
+		       struct mausb_event *event, u64 event_id)
+{
+	unsigned long flags;
+	struct mausb_completion *mausb_completion;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_for_each_entry(mausb_completion, &dev->completion_events,
+			    list_entry) {
+		if (mausb_completion->event_id == event_id) {
+			memcpy(mausb_completion->mausb_event, event,
+			       sizeof(*event));
+			complete(mausb_completion->completion_event);
+			spin_unlock_irqrestore(&dev->completion_events_lock,
+					       flags);
+			return 0;
+		}
+	}
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+
+	return -ETIMEDOUT;
+}
+
+void mausb_reset_connection_timer(struct mausb_device *dev)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&dev->connection_timer_lock, flags);
+	dev->receive_failures_num = 0;
+
+	mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+	spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+}
+
+static int mausb_start_heartbeat_timer(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (++mss.missed_heartbeats > MAUSB_MAX_MISSED_HEARTBEATS) {
+		mausb_pr_err("Missed more than %d heartbeats",
+			     MAUSB_MAX_MISSED_HEARTBEATS);
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+	mod_timer(&mss.heartbeat_timer,
+		  jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
+	return 0;
+}
+
+void mausb_reset_heartbeat_cnt(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	mss.missed_heartbeats = 0;
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
+{
+	struct mausb_urb_ctx *urb_ctx =
+			container_of(dequeue_work, struct mausb_urb_ctx, work);
+	struct urb		  *urb = urb_ctx->urb;
+	struct mausb_endpoint_ctx *ep_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_event	  mausb_event;
+	int status = 0;
+
+	ep_ctx = urb->ep->hcpriv;
+	ma_dev = ep_ctx->ma_dev;
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - finish urb immediately urb=%p, ep_handle=%#x, dev_handle=%#x",
+			     urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
+		goto complete_urb;
+	}
+
+	mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x",
+		       urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
+
+	memset(&mausb_event, 0, sizeof(mausb_event));
+
+	mausb_event.type   = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER;
+	mausb_event.status = 0;
+
+	mausb_event.data.transfer_type =
+				mausb_transfer_type_from_usb(&urb->ep->desc);
+	mausb_event.data.device_id     = ma_dev->id;
+	mausb_event.data.ep_handle     = ep_ctx->ep_handle;
+	mausb_event.data.urb	       = (uintptr_t)urb;
+	mausb_event.data.direction     = (usb_urb_dir_in(urb) ?
+						MAUSB_DATA_MSG_DIRECTION_IN :
+						MAUSB_DATA_MSG_DIRECTION_OUT);
+
+	if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) {
+		mausb_pr_alert("Failed to insert in tree urb=%p ep_handle=%#x, status=%d",
+			       urb, mausb_event.data.ep_handle, status);
+		goto complete_urb;
+	}
+
+	return;
+
+complete_urb:
+
+	/* Deallocate urb_ctx */
+	mausb_uninit_data_iterator(&urb_ctx->iterator);
+	kfree(urb_ctx);
+
+	urb->status	   = -EPROTO;
+	urb->actual_length = 0;
+	atomic_dec(&urb->use_count);
+	usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+}
+
+void mausb_initialize_mss(void)
+{
+	spin_lock_init(&mss.lock);
+	INIT_LIST_HEAD(&mss.madev_list);
+	INIT_LIST_HEAD(&mss.available_ring_buffers);
+
+	init_completion(&mss.empty);
+	complete(&mss.empty);
+	init_completion(&mss.rings_events.mausb_ring_has_events);
+	atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 0);
+	mss.deinit_in_progress	= false;
+	mss.ring_buffer_id	= 0;
+	mss.client_connected = false;
+	mss.missed_heartbeats = 0;
+	init_completion(&mss.client_stopped);
+	atomic_set(&mss.num_of_transitions_to_sleep, 0);
+
+	timer_setup(&mss.heartbeat_timer, mausb_heartbeat_timer_func, 0);
+}
+
+void mausb_deinitialize_mss(void)
+{
+	struct mausb_device *dev = NULL;
+	unsigned long flags = 0;
+	unsigned long timeout =
+			msecs_to_jiffies(MAUSB_CLIENT_STOPPED_TIMEOUT_MS);
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	mss.deinit_in_progress = true;
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mausb_pr_debug("Enqueue mausb_hcd_disconnect_work madev_addr=%x",
+			       dev->madev_addr);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	wait_for_completion(&mss.empty);
+	mausb_pr_debug("Waiting for completion on disconnect_event ended");
+
+	timeout = wait_for_completion_timeout(&mss.client_stopped, timeout);
+	mausb_pr_info("Remaining time after waiting for stopping client %ld",
+		      timeout);
+}
+
+int mausb_register_power_state_listener(void)
+{
+	mausb_pr_info("Registering power states listener");
+
+	mhcd->power_state_listener.notifier_call = mausb_power_state_cb;
+	return register_pm_notifier(&mhcd->power_state_listener);
+}
+
+void mausb_unregister_power_state_listener(void)
+{
+	mausb_pr_info("Un-registering power states listener");
+
+	unregister_pm_notifier(&mhcd->power_state_listener);
+}
+
+static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
+				void *data)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	mausb_pr_info("Power state callback action = %ld", action);
+	if (action == PM_SUSPEND_PREPARE || action == PM_HIBERNATION_PREPARE) {
+		/* Stop heartbeat timer */
+		del_timer_sync(&mss.heartbeat_timer);
+		mausb_pr_info("Saving state before sleep");
+		spin_lock_irqsave(&mss.lock, flags);
+		if (!list_empty(&mss.madev_list))
+			atomic_inc(&mss.num_of_transitions_to_sleep);
+
+		list_for_each_entry(dev, &mss.madev_list, list_entry) {
+			mausb_pr_info("Enqueue heartbeat_work madev_addr=%x",
+				      dev->madev_addr);
+			queue_work(dev->workq, &dev->heartbeat_work);
+		}
+
+		spin_unlock_irqrestore(&mss.lock, flags);
+	} else if (action == PM_POST_SUSPEND || action == PM_POST_HIBERNATION) {
+		mausb_reset_heartbeat_cnt();
+		/* Start hearbeat timer */
+		mod_timer(&mss.heartbeat_timer, jiffies +
+			  msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+	}
+	return NOTIFY_OK;
+}
+
+static void mausb_populate_standard_ep_descriptor(struct usb_ep_desc *std_desc,
+						  struct usb_endpoint_descriptor
+						  *usb_std_desc)
+{
+	std_desc->bLength	   = usb_std_desc->bLength;
+	std_desc->bDescriptorType  = usb_std_desc->bDescriptorType;
+	std_desc->bEndpointAddress = usb_std_desc->bEndpointAddress;
+	std_desc->bmAttributes	   = usb_std_desc->bmAttributes;
+	std_desc->wMaxPacketSize   = usb_std_desc->wMaxPacketSize;
+	std_desc->bInterval	   = usb_std_desc->bInterval;
+}
+
+static void
+mausb_populate_superspeed_ep_descriptor(struct usb_ss_ep_comp_desc *ss_desc,
+					struct usb_ss_ep_comp_descriptor*
+					usb_ss_desc)
+{
+	ss_desc->bLength	   = usb_ss_desc->bLength;
+	ss_desc->bDescriptorType   = usb_ss_desc->bDescriptorType;
+	ss_desc->bMaxBurst	   = usb_ss_desc->bMaxBurst;
+	ss_desc->bmAttributes	   = usb_ss_desc->bmAttributes;
+	ss_desc->wBytesPerInterval = usb_ss_desc->wBytesPerInterval;
+}
+
+void
+mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *std_desc,
+				  struct usb_endpoint_descriptor *usb_std_desc)
+{
+	mausb_populate_standard_ep_descriptor(&std_desc->usb20, usb_std_desc);
+}
+
+void
+mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *ss_desc,
+				    struct usb_endpoint_descriptor *
+				    usb_std_desc,
+				    struct usb_ss_ep_comp_descriptor *
+				    usb_ss_desc)
+{
+	mausb_populate_standard_ep_descriptor(&ss_desc->usb20, usb_std_desc);
+	mausb_populate_superspeed_ep_descriptor(&ss_desc->usb31, usb_ss_desc);
+}
+
+struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr)
+{
+	struct mausb_device *dev = NULL;
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		if (dev->madev_addr == madev_addr)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static inline
+struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
+					    enum mausb_channel channel)
+{
+	if (channel >= MAUSB_CHANNEL_MAP_LENGTH)
+		return NULL;
+
+	return ma_dev->channel_map[channel];
+}
+
+int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num,
+		    struct mausb_kvec_data_wrapper *data)
+{
+	struct mausb_ip_ctx *channel = mausb_get_data_channel(dev, channel_num);
+	int status = 0;
+
+	if (!channel)
+		return -ECHRNG;
+
+	status = mausb_ip_send(channel, data);
+
+	if (status < 0) {
+		mausb_pr_err("Send failed. Disconnecting... status=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	return status;
+}
+
+int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct ma_usb_hdr_common *ack_hdr;
+	struct kvec kvec;
+	struct mausb_kvec_data_wrapper data_to_send;
+	enum mausb_channel channel;
+
+	ack_hdr = (struct ma_usb_hdr_common *)(&event->data.hdr_ack);
+
+	data_to_send.kvec	    = &kvec;
+	data_to_send.kvec->iov_base = ack_hdr;
+	data_to_send.kvec->iov_len  = ack_hdr->length;
+	data_to_send.kvec_num	    = 1;
+	data_to_send.length	    = ack_hdr->length;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
+
+	if (event->status != 0) {
+		mausb_pr_err("Event %d failed with status %d",
+			     event->type, event->status);
+		mausb_complete_urb(event);
+		return event->status;
+	}
+
+	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
+
+	if (!urb_ctx) {
+		/* Transfer will be deleted from dequeue task */
+		mausb_pr_warn("Urb is already cancelled for event=%d",
+			      event->type);
+		return status;
+	}
+
+	return status;
+}
+
+int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	int status = 0;
+	struct mausb_urb_ctx *urb_ctx;
+
+	mausb_pr_debug("Direction=%d", event->data.direction);
+
+	if (!mausb_isoch_data_event(event)) {
+		status = mausb_send_transfer_ack(dev, event);
+		if (status < 0) {
+			mausb_pr_warn("Sending acknowledgment failed");
+			goto cleanup;
+		}
+	}
+
+	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
+
+	if (!urb_ctx)
+		mausb_pr_warn("Urb is already cancelled");
+
+cleanup:
+	mausb_release_event_resources(event);
+	return status;
+}
+
+int mausb_add_data_chunk(void *buffer, u32 buffer_size,
+			 struct list_head *chunks_list)
+{
+	struct mausb_payload_chunk *data_chunk;
+
+	data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+	if (!data_chunk)
+		return -ENOMEM;
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	INIT_LIST_HEAD(&data_chunk->list_entry);
+
+	data_chunk->kvec.iov_base = buffer;
+	data_chunk->kvec.iov_len  = buffer_size;
+	list_add_tail(&data_chunk->list_entry, chunks_list);
+	return 0;
+}
+
+int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data,
+			    struct list_head *chunks_list,
+			    u32 num_of_data_chunks)
+{
+	struct mausb_payload_chunk *data_chunk = NULL;
+	struct mausb_payload_chunk *tmp = NULL;
+	u32 current_kvec = 0;
+
+	data->length = 0;
+	data->kvec = kcalloc(num_of_data_chunks, sizeof(struct kvec),
+			     GFP_KERNEL);
+	if (!data->kvec)
+		return -ENOMEM;
+
+	list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) {
+		data->kvec[current_kvec].iov_base =
+			data_chunk->kvec.iov_base;
+		data->kvec[current_kvec].iov_len =
+		    data_chunk->kvec.iov_len;
+		++data->kvec_num;
+		data->length += data_chunk->kvec.iov_len;
+		++current_kvec;
+	}
+	return 0;
+}
+
+void mausb_cleanup_chunks_list(struct list_head *chunks_list)
+{
+	struct mausb_payload_chunk *data_chunk = NULL;
+	struct mausb_payload_chunk *tmp = NULL;
+
+	list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) {
+		list_del(&data_chunk->list_entry);
+		kfree(data_chunk);
+	}
+}
+
+static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
+				     u32 byte_num,
+				     struct list_head *data_chunks_list,
+				     u32 *data_chunks_num)
+{
+	u32 rem_data		= 0;
+	u32 bytes_to_read	= 0;
+	struct mausb_payload_chunk *data_chunk = NULL;
+
+	(*data_chunks_num) = 0;
+
+	if (!data_chunks_list)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(data_chunks_list);
+	rem_data      = iterator->length - iterator->offset;
+	bytes_to_read = min(byte_num, rem_data);
+
+	if (bytes_to_read == 0)
+		return 0;
+
+	data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+
+	if (!data_chunk)
+		return -ENOMEM;
+
+	++(*data_chunks_num);
+
+	data_chunk->kvec.iov_base = (u8 *)(iterator->buffer) + iterator->offset;
+	data_chunk->kvec.iov_len = bytes_to_read;
+	iterator->offset += bytes_to_read;
+
+	list_add_tail(&data_chunk->list_entry, data_chunks_list);
+
+	return 0;
+}
+
+static int mausb_read_scatterlist_buffer(struct mausb_data_iter *iterator,
+					 u32 byte_num,
+					 struct list_head *data_chunks_list,
+					 u32 *data_chunks_num)
+{
+	u32 current_sg_read_num;
+	struct mausb_payload_chunk *data_chunk = NULL;
+
+	(*data_chunks_num) = 0;
+
+	if (!data_chunks_list)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(data_chunks_list);
+
+	while (byte_num) {
+		if (iterator->sg_iter.consumed == iterator->sg_iter.length) {
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		}
+
+		data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+		if (!data_chunk) {
+			sg_miter_stop(&iterator->sg_iter);
+			return -ENOMEM;
+		}
+
+		current_sg_read_num = min((size_t)byte_num,
+					  iterator->sg_iter.length -
+					  iterator->sg_iter.consumed);
+
+		data_chunk->kvec.iov_base = (u8 *)iterator->sg_iter.addr +
+				iterator->sg_iter.consumed;
+		data_chunk->kvec.iov_len  = current_sg_read_num;
+
+		++(*data_chunks_num);
+		list_add_tail(&data_chunk->list_entry, data_chunks_list);
+
+		byte_num -= current_sg_read_num;
+		iterator->sg_iter.consumed += current_sg_read_num;
+		data_chunk = NULL;
+	}
+
+	return 0;
+}
+
+static u32 mausb_write_virtual_buffer(struct mausb_data_iter *iterator,
+				      void *buffer, u32 size)
+{
+	u32 rem_space   = 0;
+	u32 write_count = 0;
+
+	if (!buffer || !size)
+		return write_count;
+
+	rem_space   = iterator->length - iterator->offset;
+	write_count = min(size, rem_space);
+
+	if (write_count > 0) {
+		void *location = shift_ptr(iterator->buffer, iterator->offset);
+
+		memcpy(location, buffer, write_count);
+		iterator->offset += write_count;
+	}
+
+	return write_count;
+}
+
+static u32 mausb_write_scatterlist_buffer(struct mausb_data_iter *iterator,
+					  void *buffer, u32 size)
+{
+	u32 current_sg_rem_space;
+	u32 count = 0;
+	u32 total_count = 0;
+	void *location = NULL;
+
+	if (!buffer || !size)
+		return count;
+
+	while (size) {
+		if (iterator->sg_iter.consumed >= iterator->sg_iter.length) {
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		}
+
+		current_sg_rem_space = (u32)(iterator->sg_iter.length -
+			iterator->sg_iter.consumed);
+
+		count = min(size, current_sg_rem_space);
+		total_count += count;
+
+		location = shift_ptr(iterator->sg_iter.addr,
+				     iterator->sg_iter.consumed);
+
+		memcpy(location, buffer, count);
+
+		buffer = shift_ptr(buffer, count);
+		size -= count;
+		iterator->sg_iter.consumed += count;
+	}
+
+	return total_count;
+}
+
+int mausb_data_iterator_read(struct mausb_data_iter *iterator,
+			     u32 byte_num,
+			     struct list_head *data_chunks_list,
+			     u32 *data_chunks_num)
+{
+	if (iterator->buffer)
+		return mausb_read_virtual_buffer(iterator, byte_num,
+						 data_chunks_list,
+						 data_chunks_num);
+	else
+		return mausb_read_scatterlist_buffer(iterator, byte_num,
+						     data_chunks_list,
+						     data_chunks_num);
+}
+
+u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer,
+			      u32 length)
+{
+	if (iterator->buffer)
+		return mausb_write_virtual_buffer(iterator, buffer, length);
+	else
+		return mausb_write_scatterlist_buffer(iterator, buffer, length);
+}
+
+static inline void mausb_seek_virtual_buffer(struct mausb_data_iter *iterator,
+					     u32 seek_delta)
+{
+	iterator->offset += min(seek_delta, iterator->length -
+					    iterator->offset);
+}
+
+static void mausb_seek_scatterlist_buffer(struct mausb_data_iter *iterator,
+					  u32 seek_delta)
+{
+	u32 rem_bytes_in_current_scatter;
+
+	while (seek_delta) {
+		rem_bytes_in_current_scatter = (u32)(iterator->sg_iter.length -
+						iterator->sg_iter.consumed);
+		if (rem_bytes_in_current_scatter <= seek_delta) {
+			iterator->sg_iter.consumed +=
+			    rem_bytes_in_current_scatter;
+			seek_delta -= rem_bytes_in_current_scatter;
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		} else {
+			iterator->sg_iter.consumed += seek_delta;
+			break;
+		}
+	}
+}
+
+void mausb_data_iterator_seek(struct mausb_data_iter *iterator,
+			      u32 seek_delta)
+{
+	if (iterator->buffer)
+		mausb_seek_virtual_buffer(iterator, seek_delta);
+	else
+		mausb_seek_scatterlist_buffer(iterator, seek_delta);
+}
+
+static void mausb_calculate_buffer_length(struct mausb_data_iter *iterator)
+{
+	/* Calculate buffer length */
+	if (iterator->buffer_len > 0) {
+		/* Transfer_buffer_length is populated */
+		iterator->length = iterator->buffer_len;
+	} else if (iterator->sg && iterator->num_sgs != 0) {
+		/* Transfer_buffer_length is not populated */
+		sg_miter_start(&iterator->sg_iter, iterator->sg,
+			       iterator->num_sgs, iterator->flags);
+		while (sg_miter_next(&iterator->sg_iter))
+			iterator->length += (u32)iterator->sg_iter.length;
+		sg_miter_stop(&iterator->sg_iter);
+	} else {
+		iterator->length = 0;
+	}
+}
+
+void mausb_init_data_iterator(struct mausb_data_iter *iterator, void *buffer,
+			      u32 buffer_len, struct scatterlist *sg,
+			      unsigned int num_sgs, bool direction)
+{
+	iterator->offset = 0;
+	iterator->buffer     = buffer;
+	iterator->buffer_len = buffer_len;
+	iterator->length     = 0;
+	iterator->sg	     = sg;
+	iterator->num_sgs    = num_sgs;
+	iterator->sg_started = false;
+
+	mausb_calculate_buffer_length(iterator);
+
+	if (!buffer && sg && num_sgs != 0) {
+		/* Scatterlist provided */
+		iterator->flags = direction ? SG_MITER_TO_SG : SG_MITER_FROM_SG;
+		sg_miter_start(&iterator->sg_iter, sg, num_sgs,
+			       iterator->flags);
+		iterator->sg_started = true;
+	}
+}
+
+void mausb_uninit_data_iterator(struct mausb_data_iter *iterator)
+{
+	iterator->offset     = 0;
+	iterator->length     = 0;
+	iterator->buffer     = NULL;
+	iterator->buffer_len = 0;
+
+	if (iterator->sg_started)
+		sg_miter_stop(&iterator->sg_iter);
+
+	iterator->sg_started = false;
+}
+
+void mausb_reset_data_iterator(struct mausb_data_iter *iterator)
+{
+	iterator->offset = 0;
+	if (iterator->sg_started) {
+		sg_miter_stop(&iterator->sg_iter);
+		iterator->sg_started = false;
+	}
+
+	if (!iterator->buffer && iterator->sg && iterator->num_sgs != 0) {
+		sg_miter_start(&iterator->sg_iter, iterator->sg,
+			       iterator->num_sgs, iterator->flags);
+		iterator->sg_started = true;
+	}
+}
+
+u32 mausb_data_iterator_length(struct mausb_data_iter *iterator)
+{
+	return iterator->length;
+}
diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h
new file mode 100644
index 000000000000..f184bbc07783
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal.h
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_H__
+#define __MAUSB_HPAL_H__
+
+#include <linux/kref.h>
+#include <linux/suspend.h>
+#include <linux/usb.h>
+
+#include "ip_link.h"
+#include "mausb_address.h"
+#include "mausb_event.h"
+
+#define MAUSB_CONTROL_SETUP_SIZE	8
+#define MAUSB_BUSY_RETRIES_COUNT	3
+#define MAUSB_HEARTBEAT_TIMEOUT_MS	1000
+#define MAUSB_CLIENT_STOPPED_TIMEOUT_MS	3000
+
+#define MAUSB_MAX_RECEIVE_FAILURES	3
+#define MAUSB_MAX_MISSED_HEARTBEATS	3
+#define MAUSB_TRANSFER_RESERVED		0
+
+#define MAUSB_CHANNEL_MAP_LENGTH	4
+
+extern struct mss mss;
+extern struct mausb_hcd *mhcd;
+
+enum mausb_isoch_header_format_size {
+	MAUSB_ISOCH_SHORT_FORMAT_SIZE	 = 4,
+	MAUSB_ISOCH_STANDARD_FORMAT_SIZE = 8,
+	MAUSB_ISOCH_LONG_FORMAT_SIZE	 = 12
+};
+
+struct mausb_completion {
+	struct list_head   list_entry;
+	struct completion  *completion_event;
+	struct mausb_event *mausb_event;
+	u64		   event_id;
+};
+
+struct mausb_mss_rings_events {
+	atomic_t	  mausb_stop_reading_ring_events;
+	struct completion mausb_ring_has_events;
+};
+
+struct mss {
+	bool	   deinit_in_progress;
+	spinlock_t lock;	/* Protect mss structure */
+	u64	   ring_buffer_id;
+
+	struct completion empty;
+	struct completion client_stopped;
+	bool		  client_connected;
+	struct timer_list heartbeat_timer;
+	u8		  missed_heartbeats;
+
+	struct list_head  madev_list;
+	atomic_t	  num_of_transitions_to_sleep;
+	struct list_head  available_ring_buffers;
+
+	struct mausb_mss_rings_events	 rings_events;
+	struct mausb_events_notification events[MAUSB_MAX_NUM_OF_MA_DEVS];
+};
+
+struct mausb_device {
+	struct mausb_device_address dev_addr;
+	struct net		    *net_ns;
+	struct list_head	    list_entry;
+
+	struct mausb_ip_ctx *mgmt_channel;
+	struct mausb_ip_ctx *ctrl_channel;
+	struct mausb_ip_ctx *bulk_channel;
+	struct mausb_ip_ctx *isoch_channel;
+	struct mausb_ip_ctx *channel_map[MAUSB_CHANNEL_MAP_LENGTH];
+
+	struct work_struct work;
+	struct work_struct socket_disconnect_work;
+	struct work_struct hcd_disconnect_work;
+	struct work_struct madev_delete_work;
+	struct work_struct ping_work;
+	struct work_struct heartbeat_work;
+	struct workqueue_struct *workq;
+
+	struct kref refcount;
+	/* Set on port change event after cap resp */
+	u8 dev_type;
+	u8 dev_speed;
+	u8 lse;
+	u8 madev_addr;
+	u8 dev_connected;
+	u16 id;
+	u16 port_number;
+
+	u64		event_id;
+	spinlock_t	event_id_lock; /* Lock event ID increments */
+
+	struct list_head completion_events;
+	spinlock_t	 completion_events_lock; /* Lock completion events */
+
+	struct completion user_finished_event;
+	u16		  num_of_user_events;
+	u16		  num_of_completed_events;
+
+	spinlock_t	  num_of_user_events_lock; /* Lock user events count */
+
+	struct timer_list connection_timer;
+	u8		  receive_failures_num;
+	spinlock_t	  connection_timer_lock; /* Lock connection timer */
+
+	atomic_t	  unresponsive_client;
+
+	atomic_t	  num_of_usb_devices;
+};
+
+struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb);
+struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
+							    int status);
+struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr);
+
+static inline u64 mausb_event_id(struct mausb_device *dev)
+{
+	unsigned long flags;
+	u64 val;
+
+	spin_lock_irqsave(&dev->event_id_lock, flags);
+	val = ++(dev->event_id);
+	spin_unlock_irqrestore(&dev->event_id_lock, flags);
+
+	return val;
+}
+
+int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
+				 struct urb *request);
+int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
+		       u64 event_id);
+int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep);
+
+static inline void mausb_insert_event(struct mausb_device *dev,
+				      struct mausb_completion *event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_add_tail(&event->list_entry, &dev->completion_events);
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+}
+
+static inline void mausb_remove_event(struct mausb_device *dev,
+				      struct mausb_completion *event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_del(&event->list_entry);
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+}
+
+void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
+void mausb_complete_urb(struct mausb_event *event);
+void mausb_reset_connection_timer(struct mausb_device *dev);
+void mausb_reset_heartbeat_cnt(void);
+void mausb_release_event_resources(struct mausb_event  *event);
+void mausb_initialize_mss(void);
+void mausb_deinitialize_mss(void);
+int mausb_register_power_state_listener(void);
+void mausb_unregister_power_state_listener(void);
+
+void mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *
+				       std_desc,
+				       struct usb_endpoint_descriptor *
+				       usb_std_desc);
+void mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *
+					 ss_desc,
+					 struct usb_endpoint_descriptor *
+					 usb_std_desc,
+					 struct usb_ss_ep_comp_descriptor *
+					 usb_ss_desc);
+
+int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num,
+		    struct mausb_kvec_data_wrapper *data);
+
+int mausb_send_transfer_ack(struct mausb_device *dev,
+			    struct mausb_event *event);
+
+int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event);
+
+int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event);
+
+int mausb_add_data_chunk(void *buffer, u32 buffer_size,
+			 struct list_head *chunks_list);
+
+int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data,
+			    struct list_head *chunks_list,
+			    u32 num_of_data_chunks);
+
+void mausb_cleanup_chunks_list(struct list_head *chunks_list);
+
+static inline bool mausb_ctrl_transfer(struct ma_usb_hdr_common *hdr)
+{
+	return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL;
+}
+
+static inline bool mausb_isoch_transfer(struct ma_usb_hdr_common *hdr)
+{
+	return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH;
+}
+
+static inline bool mausb_ctrl_data_event(struct mausb_event *event)
+{
+	return event->data.transfer_type ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL;
+}
+
+static inline bool mausb_isoch_data_event(struct mausb_event *event)
+{
+	return event->data.transfer_type ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH;
+}
+
+/* usb to mausb transfer type */
+static inline
+u8 mausb_transfer_type_from_usb(struct usb_endpoint_descriptor *epd)
+{
+	return (u8)usb_endpoint_type(epd) << 3;
+}
+
+static inline u8 mausb_transfer_type_from_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK;
+}
+
+static inline
+enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type)
+{
+	return transfer_type >> 3;
+}
+
+struct mausb_data_iter {
+	u32 length;
+
+	void *buffer;
+	u32  buffer_len;
+	u32  offset;
+
+	struct scatterlist	*sg;
+	struct sg_mapping_iter	sg_iter;
+	bool		sg_started;
+	unsigned int	num_sgs;
+	unsigned int	flags;
+};
+
+struct mausb_payload_chunk {
+	struct list_head list_entry;
+	struct kvec	 kvec;
+};
+
+int mausb_data_iterator_read(struct mausb_data_iter *iterator,
+			     u32 byte_num,
+			     struct list_head *data_chunks_list,
+			     u32 *data_chunks_num);
+
+u32 mausb_data_iterator_length(struct mausb_data_iter *iterator);
+u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer,
+			      u32 length);
+
+void mausb_init_data_iterator(struct mausb_data_iter *iterator,
+			      void *buffer, u32 buffer_len,
+			      struct scatterlist *sg, unsigned int num_sgs,
+			      bool direction);
+void mausb_reset_data_iterator(struct mausb_data_iter *iterator);
+void mausb_uninit_data_iterator(struct mausb_data_iter *iterator);
+void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta);
+
+static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
+						unsigned int elem_size)
+{
+	unsigned int num_of_pages = DIV_ROUND_UP(num_of_elems * elem_size,
+						 PAGE_SIZE);
+	unsigned int order = (unsigned int)ilog2(num_of_pages) +
+					(is_power_of_2(num_of_pages) ? 0 : 1);
+	return order;
+}
+
+#endif /* __MAUSB_HPAL_H__ */
diff --git a/drivers/usb/mausb_host/ma_usb.h b/drivers/usb/mausb_host/ma_usb.h
new file mode 100644
index 000000000000..65f6229c0dfe
--- /dev/null
+++ b/drivers/usb/mausb_host/ma_usb.h
@@ -0,0 +1,869 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MA_USB_H__
+#define __MAUSB_MA_USB_H__
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <types.h>
+#endif /* __KERNEL__ */
+
+#define MA_USB_SET_FIELD_(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, _v)
+#define MA_USB_GET_FIELD_(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, _v)
+#define MA_USB_SET_FIELD(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, \
+						    MA_USB_##_v)
+#define MA_USB_GET_FIELD(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, \
+						    MA_USB_##_v)
+
+#define MA_USB_MGMT_TOKEN_RESERVED  0
+#define MA_USB_MGMT_TOKEN_MIN       1
+#define MA_USB_MGMT_TOKEN_MAX       ((1 << 10) - 1)
+
+#define MA_USB_DATA_EPS_UNASSIGNED  0
+#define MA_USB_DATA_EPS_ACTIVE      1
+#define MA_USB_DATA_EPS_INACTIVE    2
+#define MA_USB_DATA_EPS_HALTED      3
+
+#define MA_USB_DATA_TFLAGS_ARQ      1
+#define MA_USB_DATA_TFLAGS_NEG      2
+#define MA_USB_DATA_TFLAGS_EOT      4
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL   0
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH  8
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_BULK   16
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_INTR   24
+
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK   0x18
+
+#define MA_USB_DATA_IFLAGS_MTD_VALID      1
+#define MA_USB_DATA_IFLAGS_HDR_FMT_SHORT  0
+#define MA_USB_DATA_IFLAGS_HDR_FMT_STD    2
+#define MA_USB_DATA_IFLAGS_HDR_FMT_LONG   4
+#define MA_USB_DATA_IFLAGS_IRS_FMT_STD    0
+#define MA_USB_DATA_IFLAGS_IRS_FMT_LONG   2
+#define MA_USB_DATA_IFLAGS_ASAP           8
+
+#define MA_USB_DATA_IFLAGS_FMT_MASK       0x6
+
+/* version */
+
+#define MA_USB_HDR_VERSION_1_0      0
+
+/* flags */
+
+#define MA_USB_HDR_FLAGS_HOST       1
+#define MA_USB_HDR_FLAGS_RETRY      2
+#define MA_USB_HDR_FLAGS_TIMESTAMP  4
+#define MA_USB_HDR_FLAGS_RESERVED   8
+#define MA_USB_HDR_FLAG(_f) MA_USB_HDR_FLAGS_##_f
+
+/* type and subtype */
+
+#define MA_USB_HDR_TYPE_TYPE_MASK     0xC0
+#define MA_USB_HDR_TYPE_SUBTYPE_MASK  0x3F
+
+#define MA_USB_HDR_TYPE_TYPE_MANAGEMENT 0
+#define MA_USB_HDR_TYPE_TYPE_CONTROL    1
+#define MA_USB_HDR_TYPE_TYPE_DATA       2
+
+/* Management subtypes */
+
+#define _MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2)
+#define _MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 1)
+
+#define MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \
+	_MA_USB_HDR_TYPE_MANAGEMENT_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \
+	_MA_USB_HDR_TYPE_MANAGEMENT_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+
+#define MA_USB_HDR_TYPE_SUBTYPE_CAP               0
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVHANDLE      1
+#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLE          2
+#define MA_USB_HDR_TYPE_SUBTYPE_EPACTIVATE        3
+#define MA_USB_HDR_TYPE_SUBTYPE_EPINACTIVATE      4
+#define MA_USB_HDR_TYPE_SUBTYPE_EPRESET           5
+#define MA_USB_HDR_TYPE_SUBTYPE_CLEARTRANSFERS    6
+#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLEDELETE    7
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVRESET          8
+#define MA_USB_HDR_TYPE_SUBTYPE_MODIFYEP0         9
+#define MA_USB_HDR_TYPE_SUBTYPE_SETUSBDEVADDR     10
+#define MA_USB_HDR_TYPE_SUBTYPE_UPDATEDEV         11
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVDISCONNECT  12
+#define MA_USB_HDR_TYPE_SUBTYPE_USBSUSPEND        13
+#define MA_USB_HDR_TYPE_SUBTYPE_USBRESUME         14
+#define MA_USB_HDR_TYPE_SUBTYPE_REMOTEWAKE        15
+#define MA_USB_HDR_TYPE_SUBTYPE_PING              16
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVDISCONNECT     17
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVINITDISCONNECT 18
+#define MA_USB_HDR_TYPE_SUBTYPE_SYNCH             19
+#define MA_USB_HDR_TYPE_SUBTYPE_CANCELTRANSFER    20
+#define MA_USB_HDR_TYPE_SUBTYPE_EPOPENSTREAM      21
+#define MA_USB_HDR_TYPE_SUBTYPE_EPCLOSESTREAM     22
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVRESET       23
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVNOTIFICATION   24
+#define MA_USB_HDR_TYPE_SUBTYPE_EPSETKEEPALIVE    25
+#define MA_USB_HDR_TYPE_SUBTYPE_GETPORTBW         26
+#define MA_USB_HDR_TYPE_SUBTYPE_SLEEP             27
+#define MA_USB_HDR_TYPE_SUBTYPE_WAKE              28
+#define MA_USB_HDR_TYPE_SUBTYPE_VENDORSPECIFIC    31 /* Reserved */
+
+/* Data subtypes */
+
+#define _MA_USB_HDR_TYPE_DATA_REQ(_s) ({ \
+	typeof(_s) (s) = (_s); \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 \
+	+ ((s) > 0 ? 1 : 0)); })
+#define _MA_USB_HDR_TYPE_DATA_RESP(_s) ({ \
+	typeof(_s) (s) = (_s); \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 + 1 \
+	+ ((s) > 0 ? 1 : 0)); })
+#define _MA_USB_HDR_TYPE_DATA_ACK(_s) ( \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 2))
+
+#define MA_USB_HDR_TYPE_DATA_REQ(_s) \
+	_MA_USB_HDR_TYPE_DATA_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_DATA_RESP(_s) \
+	_MA_USB_HDR_TYPE_DATA_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_DATA_ACK(_s) \
+	_MA_USB_HDR_TYPE_DATA_ACK(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+
+#define MA_USB_HDR_TYPE_SUBTYPE_TRANSFER          0
+#define MA_USB_HDR_TYPE_SUBTYPE_ISOCHTRANSFER     1
+
+/* EP/Device Handle */
+
+#define MA_USB_DEVICE_HANDLE_RESERVED   0
+
+#define MA_USB_EP_HANDLE_D_MASK     0x0001
+#define MA_USB_EP_HANDLE_EP_N_MASK  0x001e
+#define MA_USB_EP_HANDLE_ADDR_MASK  0x0fe0
+#define MA_USB_EP_HANDLE_BUS_N_MASK 0xf000
+
+#define MA_USB_EP_HANDLE(_b, _a, _e, _d) ( \
+	MA_USB_SET_FIELD_(EP_HANDLE_BUS_N, _b)  | \
+	MA_USB_SET_FIELD_(EP_HANDLE_ADDR, _a)   | \
+	MA_USB_SET_FIELD_(EP_HANDLE_EP_N, _e)   | \
+	MA_USB_SET_FIELD_(EP_HANDLE_D, _d))
+
+#define MA_USB_EP_HANDLE_BUS_N_VIRTUAL  15
+#define MA_USB_EP_HANDLE_ADDR_DEFAULT   0
+#define MA_USB_EP_HANDLE_EP_N_DEFAULT   0
+#define MA_USB_EP_HANDLE_D_OUT          0	/* See USB2.0 9.3, Table 9-2 */
+#define MA_USB_EP_HANDLE_D_IN           1	/* See USB2.0 9.3, Table 9-2 */
+
+/* Status codes */
+
+#define MA_USB_HDR_STATUS_UNSUCCESSFUL                  -128
+#define MA_USB_HDR_STATUS_INVALID_MA_USB_SESSION_STATE  -127
+#define MA_USB_HDR_STATUS_INVALID_DEVICE_HANDLE         -126
+#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE             -125
+#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE_STATE       -124
+#define MA_USB_HDR_STATUS_INVALID_REQUEST               -123
+#define MA_USB_HDR_STATUS_MISSING_SEQUENCE_NUMBER       -122
+#define MA_USB_HDR_STATUS_TRANSFER_PENDING              -121
+#define MA_USB_HDR_STATUS_TRANSFER_EP_STALL             -120
+#define MA_USB_HDR_STATUS_TRANSFER_SIZE_ERROR           -119
+#define MA_USB_HDR_STATUS_TRANSFER_DATA_BUFFER_ERROR    -118
+#define MA_USB_HDR_STATUS_TRANSFER_BABBLE_DETECTED      -117
+#define MA_USB_HDR_STATUS_TRANSFER_TRANSACTION_ERROR    -116
+#define MA_USB_HDR_STATUS_TRANSFER_SHORT_TRANSFER       -115
+#define MA_USB_HDR_STATUS_TRANSFER_CANCELED             -114
+#define MA_USB_HDR_STATUS_INSUFICIENT_RESOURCES         -113
+#define MA_USB_HDR_STATUS_NOT_SUFFICIENT_BANDWIDTH      -112
+#define MA_USB_HDR_STATUS_INTERNAL_ERROR                -111
+#define MA_USB_HDR_STATUS_DATA_OVERRUN                  -110
+#define MA_USB_HDR_STATUS_DEVICE_NOT_ACCESSED           -109
+#define MA_USB_HDR_STATUS_BUFFER_OVERRUN                -108
+#define MA_USB_HDR_STATUS_BUSY                          -107
+#define MA_USB_HDR_STATUS_DROPPED_PACKET                -106
+#define MA_USB_HDR_STATUS_ISOCH_TIME_EXPIRED            -105
+#define MA_USB_HDR_STATUS_ISOCH_TIME_INVALID            -104
+#define MA_USB_HDR_STATUS_NO_USB_PING_RESPONSE          -103
+#define MA_USB_HDR_STATUS_NOT_SUPPORTED                 -102
+#define MA_USB_HDR_STATUS_REQUEST_DENIED                -101
+#define MA_USB_HDR_STATUS_MISSING_REQUEST_ID            -100
+#define MA_USB_HDR_STATUS_SUCCESS                       0	/* Reserved */
+#define MA_USB_HDR_STATUS_NO_ERROR MA_USB_HDR_STATUS_SUCCESS	/* Reserved */
+
+/* Speed values */
+
+#define MA_USB_SPEED_LOW_SPEED         0
+#define MA_USB_SPEED_FULL_SPEED        1
+#define MA_USB_SPEED_HIGH_SPEED        2
+#define MA_USB_SPEED_SUPER_SPEED       3
+#define MA_USB_SPEED_SUPER_SPEED_PLUS  4
+
+/* capreq extra hdr */
+
+#define MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH\
+	(sizeof(struct ma_usb_desc) +\
+	sizeof(struct ma_usb_capreq_desc_synchronization))
+#define MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH\
+	(sizeof(struct ma_usb_desc) +\
+	sizeof(struct ma_usb_capreq_desc_link_sleep))
+
+#define MA_USB_CAPREQ_LENGTH\
+	(sizeof(struct ma_usb_hdr_common) +\
+	sizeof(struct ma_usb_hdr_capreq) +\
+	MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH +\
+	MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH)
+
+/* capreq desc types */
+
+#define MA_USB_CAPREQ_DESC_TYPE_SYNCHRONIZATION 3
+#define MA_USB_CAPREQ_DESC_TYPE_LINK_SLEEP      5
+
+/* capresp descriptors */
+
+#define MA_USB_CAPRESP_DESC_TYPE_SPEED            0
+#define MA_USB_CAPRESP_DESC_TYPE_P_MANAGED_OUT    1
+#define MA_USB_CAPRESP_DESC_TYPE_ISOCHRONOUS      2
+#define MA_USB_CAPRESP_DESC_TYPE_SYNCHRONIZATION  3
+#define MA_USB_CAPRESP_DESC_TYPE_CONTAINER_ID     4
+#define MA_USB_CAPRESP_DESC_TYPE_LINK_SLEEP       5
+#define MA_USB_CAPRESP_DESC_TYPE_HUB_LATENCY      6
+
+/* Request ID and sequence number values */
+
+#define MA_USB_TRANSFER_RESERVED      0
+#define MA_USB_TRANSFER_REQID_MIN     0
+#define MA_USB_TRANSFER_REQID_MAX     ((1 <<  8) - 1)
+#define MA_USB_TRANSFER_SEQN_MIN      0
+#define MA_USB_TRANSFER_SEQN_MAX      ((1 << 24) - 2)
+#define MA_USB_TRANSFER_SEQN_INVALID  ((1 << 24) - 1)
+
+#define MA_USB_ISOCH_SFLAGS_FRAGMENT      0x1
+#define MA_USB_ISOCH_SFLAGS_LAST_FRAGMENT 0x2
+
+#define MAUSB_MAX_MGMT_SIZE 50
+
+#define MAUSB_TRANSFER_HDR_SIZE (u32)(sizeof(struct ma_usb_hdr_common) +\
+				      sizeof(struct ma_usb_hdr_transfer))
+
+#define MAUSB_ISOCH_TRANSFER_HDR_SIZE (u16)(sizeof(struct ma_usb_hdr_common) +\
+			sizeof(struct ma_usb_hdr_isochtransfer) +\
+			sizeof(struct ma_usb_hdr_isochtransfer_optional))
+
+#define MAX_ISOCH_ASAP_PACKET_SIZE (150000 /* Network MTU */ -\
+	MAUSB_ISOCH_TRANSFER_HDR_SIZE - 20 /* IP header size */ -\
+	8 /* UDP header size*/)
+
+#define shift_ptr(ptr, offset) ((u8 *)(ptr) + (offset))
+
+/* USB descriptor */
+struct ma_usb_desc {
+	u8 length;
+	u8 type;
+	u8 value[0];
+} __packed;
+
+struct ma_usb_ep_handle {
+	u16 d		:1,
+	    ep_n	:4,
+	    addr	:7,
+	    bus_n	:4;
+};
+
+struct ma_usb_hdr_mgmt {
+	u32 status	:8,
+	    token	:10,  /* requestor originator allocated */
+	    reserved	:14;
+} __aligned(4);
+
+struct ma_usb_hdr_ctrl {	/* used in all req/resp/conf operations */
+	s8 status;
+	u8 link_type;
+	union {
+		u8 tid;	/* ieee 802.11 */
+	} connection_id;
+} __aligned(4);
+
+struct ma_usb_hdr_data {
+	s8 status;
+	u8 eps		:2,
+	   t_flags	:6;
+	union {
+		u16 stream_id;
+		struct {
+			u16 headers	:12,
+			    i_flags	:4;
+		};
+	};
+} __aligned(4);
+
+struct ma_usb_hdr_common {
+	u8 version	:4,
+	   flags	:4;
+	u8  type;
+	u16 length;
+	union {
+		u16 dev;
+		u16 epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u8 dev_addr;
+	u8 ssid;
+	union {
+		s8 status;
+		struct ma_usb_hdr_mgmt mgmt;
+		struct ma_usb_hdr_ctrl ctrl;
+		struct ma_usb_hdr_data data;
+	};
+} __aligned(4);
+
+/* capreq extra hdr */
+
+struct ma_usb_hdr_capreq {
+	u32 out_mgmt_reqs	:12,
+	    reserved		:20;
+} __aligned(4);
+
+struct ma_usb_capreq_desc_synchronization {
+	u8 media_time_available	:1,
+	   reserved		:7;
+} __packed;
+
+struct ma_usb_capreq_desc_link_sleep {
+	u8 link_sleep_capable	:1,
+	   reserved		:7;
+} __packed;
+
+/* capresp extra hdr */
+
+struct ma_usb_hdr_capresp {
+	u16 endpoints;
+	u8 devices;
+	u8 streams		:5,
+	   dev_type		:3;
+	u32 descs		:8,
+	    descs_length	:24;
+	u16 out_transfer_reqs;
+	u16 out_mgmt_reqs	:12,
+	    reserved		:4;
+} __aligned(4);
+
+struct ma_usb_capresp_desc_speed {
+	u8 reserved1		:4,
+		speed		:4;
+	u8 reserved2		:4,
+	   lse			:2,	/* USB3.1 8.5.6.7, Table 8-22 */
+	   reserved3		:2;
+} __packed;
+
+struct ma_usb_capresp_desc_p_managed_out {
+	u8 elastic_buffer		:1,
+	   drop_notification		:1,
+	   reserved			:6;
+} __packed;
+
+struct ma_usb_capresp_desc_isochronous {
+	u8 payload_dword_aligned	:1,
+	   reserved			:7;
+} __packed;
+
+struct ma_usb_capresp_desc_synchronization {
+	u8 media_time_available	:1,
+	   time_stamp_required	:1,/* hubs need this set */
+	   reserved		:6;
+} __packed;
+
+struct ma_usb_capresp_desc_container_id {
+	u8 container_id[16];	/* UUID IETF RFC 4122 */
+} __packed;
+
+struct ma_usb_capresp_desc_link_sleep {
+	u8 link_sleep_capable	:1,
+	   reserved		:7;
+} __packed;
+
+struct ma_usb_capresp_desc_hub_latency {
+	u16 latency;		/* USB3.1 */
+} __packed;
+
+/* usbdevhandlereq extra hdr */
+struct ma_usb_hdr_usbdevhandlereq {
+	u32 route_string	:20,
+	    speed		:4,
+	    reserved1		:8;
+	u16 hub_dev_handle;
+	u16 reserved2;
+	u16 parent_hs_hub_dev_handle;
+	u16 parent_hs_hub_port		:4,
+	    mtt				:1,	/* USB2.0 11.14, 11.14.1.3 */
+	    lse				:2,	/* USB3.1 8.5.6.7, Table 8-22 */
+	    reserved3			:9;
+} __aligned(4);
+
+/* usbdevhandleresp extra hdr */
+struct ma_usb_hdr_usbdevhandleresp {
+	u16 dev_handle;
+	u16 reserved;
+} __aligned(4);
+
+/* ephandlereq extra hdr */
+struct ma_usb_hdr_ephandlereq {
+	u32 ep_descs		:5,
+	    ep_desc_size	:6,
+	    reserved		:21;
+} __aligned(4);
+
+/*
+ * Restricted USB2.0 ep desc limited to 6 bytes, isolating further changes.
+ * See USB2.0 9.6.6, Table 9-13
+ */
+struct usb_ep_desc {
+	u8 bLength;
+	/* USB2.0 9.4, Table 9-5 (5) usb/ch9.h: USB_DT_ENDPOINT */
+	u8 bDescriptorType;
+	u8  bEndpointAddress;
+	u8  bmAttributes;
+	__le16 wMaxPacketSize;
+	u8  bInterval;
+} __packed;
+
+/*
+ * Restricted USB3.1 ep comp desc isolating further changes in usb/ch9.h
+ * See USB3.1 9.6.7, Table 9-26
+ */
+struct usb_ss_ep_comp_desc {
+	u8 bLength;
+	/* USB3.1 9.4, Table 9-6 (48) usb/ch9.h: USB_DT_SS_ENDPOINT_COMP */
+	u8  bDescriptorType;
+	u8  bMaxBurst;
+	u8  bmAttributes;
+	__le16 wBytesPerInterval;
+} __packed;
+
+/*
+ * USB3.1 ss_plus_isoch_ep_comp_desc
+ * See USB3.1 9.6.8, Table 9-27
+ */
+struct usb_ss_plus_isoch_ep_comp_desc {
+	u8 bLength;
+	/* USB3.1 9.4, Table 9-6 (49) usb/ch9.h: not yet defined! */
+	u8 bDescriptorType;
+	u16 wReserved;
+	u32 dwBytesPerInterval;
+} __packed;
+
+struct ma_usb_ephandlereq_desc_std {
+	struct usb_ep_desc usb20;
+} __aligned(4);
+
+struct ma_usb_ephandlereq_desc_ss {
+	struct usb_ep_desc	   usb20;
+	struct usb_ss_ep_comp_desc usb31;
+} __aligned(4);
+
+struct ma_usb_ephandlereq_desc_ss_plus {
+	struct usb_ep_desc		      usb20;
+	struct usb_ss_ep_comp_desc	      usb31;
+	struct usb_ss_plus_isoch_ep_comp_desc usb31_isoch;
+} __aligned(4);
+
+struct ma_usb_dev_context {
+	struct usb_ep_desc usb;
+};
+
+/* ephandleresp extra hdr */
+struct ma_usb_hdr_ephandleresp {
+	u32 ep_descs :5,
+	    reserved :27;
+} __aligned(4);
+
+/* ephandleresp descriptor */
+struct ma_usb_ephandleresp_desc {
+	union {
+		struct ma_usb_ep_handle eph;
+		u16		epv;
+	} ep_handle;
+	u16 d		:1,	/* non-control or non-OUT */
+	    isoch	:1,
+	    l_managed	:1,	/* control or non-isoch OUT */
+	    invalid	:1,
+	    reserved1	:12;
+	u16 ccu;		/* control or non-isoch OUT */
+	u16 reserved2;
+	u32 buffer_size;	/* control or OUT */
+	u16 isoch_prog_delay;	/* in us. */
+	u16 isoch_resp_delay;	/* in us. */
+} __aligned(4);
+
+/* epactivatereq extra hdr */
+struct ma_usb_hdr_epactivatereq {
+	u32 ep_handles	:5,
+	    reserved	:27;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epactivateresp extra hdr */
+struct ma_usb_hdr_epactivateresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epinactivatereq extra hdr */
+struct ma_usb_hdr_epinactivatereq {
+	u32 ep_handles	:5,
+	    suspend	:1,
+	    reserved	:26;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epinactivateresp extra hdr */
+struct ma_usb_hdr_epinactivateresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epresetreq extra hdr */
+struct ma_usb_hdr_epresetreq {
+	u32 ep_reset_blocks	:5,
+	    reserved		:27;
+} __aligned(4);
+
+/* epresetreq reset block */
+struct ma_usb_epresetreq_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 tsp		:1,
+	    reserved	:15;
+} __aligned(4);
+
+/* epresetresp extra hdr */
+struct ma_usb_hdr_epresetresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* cleartransfersreq extra hdr */
+struct ma_usb_hdr_cleartransfersreq {
+	u32 info_blocks	:8,
+	    reserved	:24;
+} __aligned(4);
+
+/* cleartransfersreq info block */
+struct ma_usb_cleartransfersreq_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id; /* ss stream eps only */
+	u32 start_req_id	:8,
+	    reserved		:24;
+} __aligned(4);
+
+/* cleartransfersresp extra hdr */
+struct ma_usb_hdr_cleartransfersresp {
+	u32 status_blocks	:8,
+	    reserved		:24;
+} __aligned(4);
+
+/* cleartransfersresp status block */
+struct ma_usb_cleartransfersresp_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;	/* ss stream eps only */
+	u32 cancel_success	:1,
+	    partial_delivery	:1,
+	    reserved		:30;
+	u32 last_req_id		:8,
+	    delivered_seq_n	:24;	/* OUT w/partial_delivery only */
+	u32 delivered_byte_offset;	/* OUT w/partial_delivery only */
+} __aligned(4);
+
+/* ephandledeletereq extra hdr */
+struct ma_usb_hdr_ephandledeletereq {
+	u32 ep_handles	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* ephandledeleteresp extra hdr */
+struct ma_usb_hdr_ephandledeleteresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* modifyep0req extra hdr */
+struct ma_usb_hdr_modifyep0req {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 max_packet_size;
+} __aligned(4);
+
+/*
+ * modifyep0resp extra hdr
+ * Only if req ep0 handle addr was 0 and req dev is in the addressed state
+ * or  if req ep0 handle addr != 0 and req dev is in default state
+ */
+struct ma_usb_hdr_modifyep0resp {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+
+	u16 reserved;
+} __aligned(4);
+
+/* setusbdevaddrreq extra hdr */
+struct ma_usb_hdr_setusbdevaddrreq {
+	u16 response_timeout;	/* in ms. */
+	u16 reserved;
+} __aligned(4);
+
+/* setusbdevaddrresp extra hdr */
+struct ma_usb_hdr_setusbdevaddrresp {
+	u32 addr	:7,
+	    reserved	:25;
+} __aligned(4);
+
+/* updatedevreq extra hdr */
+struct ma_usb_hdr_updatedevreq {
+	u16 max_exit_latency;	/* hubs only */
+	u8 hub		:1,
+	   ports	:4,
+	   mtt		:1,
+	   ttt		:2;
+	u8 integrated_hub_latency	:1,
+	   reserved			:7;
+} __aligned(4);
+
+/*
+ * USB2.0 dev desc, isolating further changes in usb/ch9.h
+ * See USB2.0 9.6.6, Table 9-13
+ */
+struct usb_dev_desc {
+	u8 bLength;
+	/*
+	 * USB2.0 9.4, Table 9-5 (1)
+	 * usb/ch9.h: USB_DT_DEVICE
+	 */
+	u8 bDescriptorType;
+	__le16 bcdUSB;
+	u8  bDeviceClass;
+	u8  bDeviceSubClass;
+	u8  bDeviceProtocol;
+	u8  bMaxPacketSize0;
+	__le16 idVendor;
+	__le16 idProduct;
+	__le16 bcdDevice;
+	u8  iManufacturer;
+	u8  iProduct;
+	u8  iSerialNumber;
+	u8  bNumConfigurations;
+} __packed;
+
+struct ma_usb_updatedevreq_desc {
+	struct usb_dev_desc usb20;
+} __aligned(4);
+
+/* remotewakereq extra hdr */
+struct ma_usb_hdr_remotewakereq {
+	u32 resumed  :1,
+		 reserved :31;
+} __aligned(4);
+
+/* synchreq/resp extra hdr */
+struct ma_usb_hdr_synch {
+	u32 mtd_valid		:1,	/* MA-USB1.0b 6.5.1.8, Table 66 */
+	    resp_required	:1,
+	    reserved		:30;
+	union {
+		u32 timestamp;		/* MA-USB1.0b 6.5.1.11 */
+		struct {
+			u32 delta		:13,
+			    bus_interval	:19;
+		};			/* MA-USB1.0b 6.6.1, Table 69 */
+	};
+	u32 mtd;			/* MA-USB1.0b 6.5.1.12 */
+} __aligned(4);
+
+/* canceltransferreq extra hdr */
+struct ma_usb_hdr_canceltransferreq {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;
+	u32 req_id	  :8,
+		 reserved :24;
+} __aligned(4);
+
+/* canceltransferresp extra hdr */
+struct ma_usb_hdr_canceltransferresp {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;
+	u32 req_id		:8,
+	    cancel_status	:3,
+	    reserved1		:21;
+	u32 delivered_seq_n	:24,
+	    reserved2		:8;
+	u32 delivered_byte_offset;
+} __aligned(4);
+
+/* transferreq/resp/ack extra hdr */
+struct ma_usb_hdr_transfer {
+	u32 seq_n	:24,
+	    req_id	:8;
+	union {
+		u32 rem_size_credit;
+		/* ISOCH aliased fields added for convenience. */
+		struct {
+			u32 presentation_time	:20,
+			    segments		:12;
+		};
+	};
+} __aligned(4);
+
+/* isochtransferreq/resp extra hdr */
+struct ma_usb_hdr_isochtransfer {
+	u32 seq_n		:24,
+	    req_id		:8;
+	u32 presentation_time	:20,
+	    segments		:12;
+} __aligned(4);
+
+/* isochtransferreq/resp optional hdr */
+struct ma_usb_hdr_isochtransfer_optional {
+	union {
+		u32 timestamp;	/* MA-USB1.0b 6.5.1.11 */
+		struct {
+			u32 delta		:13,
+			    bus_interval	:19;
+		};		/* MA-USB1.0b 6.6.1, Table 69 */
+	};
+	u32 mtd;		/* MA-USB1.0b 6.5.1.12 */
+} __aligned(4);
+
+/* isochdatablock hdrs */
+
+struct ma_usb_hdr_isochdatablock_short {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+} __aligned(4);
+
+struct ma_usb_hdr_isochdatablock_std {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+	u16 segment_length;
+	u16 fragment_offset;
+} __aligned(4);
+
+struct ma_usb_hdr_isochdatablock_long {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+	u32 segment_length;
+	u32 fragment_offset;
+} __aligned(4);
+
+/* isochreadsizeblock hdrs */
+
+struct ma_usb_hdr_isochreadsizeblock_std {
+	u32 service_intervals		:12,
+	    max_segment_length		:20;
+} __aligned(4);
+
+struct ma_usb_hdr_isochreadsizeblock_long {
+	u32 service_intervals		:12,
+	    reserved			:20;
+	u32 max_segment_length;
+} __aligned(4);
+
+static inline int __mausb_set_field(int m, int v)
+{
+	return ((~((m) - 1) & (m)) * (v)) & (m);
+}
+
+static inline int __mausb_get_field(int m, int v)
+{
+	return ((v) & (m)) / (~((m) - 1) & (m));
+}
+
+static inline bool mausb_is_management_hdr_type(int hdr_type)
+{
+	return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type)
+			== MA_USB_HDR_TYPE_TYPE_MANAGEMENT;
+}
+
+static inline bool mausb_is_data_hdr_type(int hdr_type)
+{
+	return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type)
+			== MA_USB_HDR_TYPE_TYPE_DATA;
+}
+
+static inline bool mausb_is_management_resp_hdr_type(int hdr_resp_type)
+{
+	return mausb_is_management_hdr_type(hdr_resp_type) &&
+			(MA_USB_GET_FIELD_(HDR_TYPE_SUBTYPE, hdr_resp_type) & 1)
+			!= 0;
+}
+
+static inline
+struct ma_usb_hdr_transfer *
+mausb_get_data_transfer_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_transfer *)shift_ptr(hdr, sizeof(*hdr));
+}
+
+static inline
+struct ma_usb_hdr_isochtransfer *
+mausb_get_isochtransfer_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_isochtransfer *)shift_ptr(hdr, sizeof(*hdr));
+}
+
+static inline
+struct ma_usb_hdr_isochtransfer_optional *
+mausb_hdr_isochtransfer_optional_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_isochtransfer_optional *)
+			shift_ptr(hdr, sizeof(struct ma_usb_hdr_common) +
+				       sizeof(struct ma_usb_hdr_isochtransfer));
+}
+
+#endif	/* __MAUSB_MA_USB_H__ */
diff --git a/drivers/usb/mausb_host/mausb_address.h b/drivers/usb/mausb_host/mausb_address.h
new file mode 100644
index 000000000000..1a75482740ea
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_address.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_ADDRESS_H__
+#define __MAUSB_MAUSB_ADDRESS_H__
+
+#include <linux/inet.h>
+#include <linux/types.h>
+
+struct mausb_device_address {
+	u8 link_type;
+	struct {
+		char address[INET6_ADDRSTRLEN];
+		u8 number_of_ports;
+		struct {
+			u16 management;
+			u16 control;
+			u16 bulk;
+			u16 interrupt;
+			u16 isochronous;
+		} port;
+	} ip;
+};
+
+#endif /* __MAUSB_MAUSB_ADDRESS_H__ */
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
index 3ce90c29f6de..8730590126ea 100644
--- a/drivers/usb/mausb_host/mausb_core.c
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -12,6 +12,7 @@
 #include <linux/net.h>
 
 #include "hcd.h"
+#include "hpal.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
@@ -75,12 +76,20 @@ static int mausb_host_init(void)
 	if (status < 0)
 		goto cleanup;
 
-	status = mausb_create_dev();
+	status = mausb_register_power_state_listener();
 	if (status < 0)
 		goto cleanup_hcd;
 
+	status = mausb_create_dev();
+	if (status < 0)
+		goto unregister_power_state_listener;
+
+	mausb_initialize_mss();
+
 	return 0;
 
+unregister_power_state_listener:
+	mausb_unregister_power_state_listener();
 cleanup_hcd:
 	mausb_deinit_hcd();
 cleanup:
@@ -91,6 +100,8 @@ static int mausb_host_init(void)
 static void mausb_host_exit(void)
 {
 	mausb_pr_info("Module unloading started...");
+	mausb_unregister_power_state_listener();
+	mausb_deinitialize_mss();
 	mausb_deinit_hcd();
 	mausb_cleanup_dev(1);
 	mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION);
diff --git a/drivers/usb/mausb_host/mausb_event.h b/drivers/usb/mausb_host/mausb_event.h
new file mode 100644
index 000000000000..a574f67d789d
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_event.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_EVENT_H__
+#define __MAUSB_MAUSB_EVENT_H__
+
+#include "ma_usb.h"
+
+#define MAUSB_MAX_NUM_OF_MA_DEVS			15
+#define MAUSB_RING_BUFFER_SIZE				1024
+#define MAUSB_MAX_DATA_IN_REQ_SIZE			28
+
+#define MAUSB_EVENT_TYPE_DEV_RESET			1u
+#define MAUSB_EVENT_TYPE_USB_DEV_HANDLE			2u
+#define MAUSB_EVENT_TYPE_EP_HANDLE			3u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE		4u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE		5u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_RESET		6u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_DELETE		7u
+#define MAUSB_EVENT_TYPE_MODIFY_EP0			8u
+#define MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS		9u
+#define MAUSB_EVENT_TYPE_UPDATE_DEV			10u
+#define MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT		11u
+#define MAUSB_EVENT_TYPE_PING				12u
+#define MAUSB_EVENT_TYPE_DEV_DISCONNECT			13u
+#define MAUSB_EVENT_TYPE_USB_DEV_RESET			14u
+#define MAUSB_EVENT_TYPE_CANCEL_TRANSFER		15u
+
+#define MAUSB_EVENT_TYPE_PORT_CHANGED			80u
+#define MAUSB_EVENT_TYPE_SEND_MGMT_MSG			81u
+#define MAUSB_EVENT_TYPE_SEND_DATA_MSG			82u
+#define MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG		83u
+#define MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG		84u
+#define MAUSB_EVENT_TYPE_URB_COMPLETE			85u
+#define MAUSB_EVENT_TYPE_SEND_ACK			86u
+#define MAUSB_EVENT_TYPE_ITERATOR_RESET			87u
+#define MAUSB_EVENT_TYPE_ITERATOR_SEEK			88u
+#define MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER		89u
+#define MAUSB_EVENT_TYPE_DELETE_MA_DEV			90u
+#define MAUSB_EVENT_TYPE_USER_FINISHED			91u
+#define MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES	92u
+#define MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED		93u
+#define MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT		94u
+
+#define MAUSB_EVENT_TYPE_NONE				255u
+
+#define MAUSB_DATA_MSG_DIRECTION_OUT			0
+#define MAUSB_DATA_MSG_DIRECTION_IN			1
+#define MAUSB_DATA_MSG_CONTROL MAUSB_DATA_MSG_DIRECTION_OUT
+
+struct mausb_devhandle {
+	u64 event_id;
+	u32 route_string;
+	u16 hub_dev_handle;
+	u16 parent_hs_hub_dev_handle;
+	u16 parent_hs_hub_port;
+	u16 mtt;
+	/* dev_handle assigned in user */
+	u16 dev_handle;
+	u8  device_speed;
+	u8  lse;
+};
+
+struct mausb_ephandle {
+	u64 event_id;
+	u16 device_handle;
+	u16 descriptor_size;
+	/* ep_handle assigned in user */
+	u16 ep_handle;
+	char	 descriptor[sizeof(struct ma_usb_ephandlereq_desc_ss)];
+};
+
+struct mausb_epactivate {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_epinactivate {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_epreset {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+	u8  tsp;
+};
+
+struct mausb_epdelete {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_updatedev {
+	u64 event_id;
+	u16 device_handle;
+	u16 max_exit_latency;
+	struct ma_usb_updatedevreq_desc update_descriptor;
+	u8  hub;
+	u8  number_of_ports;
+	u8  mtt;
+	u8  ttt;
+	u8  integrated_hub_latency;
+};
+
+struct mausb_usbdevreset {
+	u64 event_id;
+	u16 device_handle;
+};
+
+struct mausb_modifyep0 {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+	__le16 max_packet_size;
+};
+
+struct mausb_setusbdevaddress {
+	u64 event_id;
+	u16 device_handle;
+	u16 response_timeout;
+};
+
+struct mausb_usbdevdisconnect {
+	u16 device_handle;
+};
+
+struct mausb_canceltransfer {
+	u64 urb;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_mgmt_hdr {
+	__aligned(4) char hdr[MAUSB_MAX_MGMT_SIZE];
+};
+
+struct mausb_mgmt_req_timedout {
+	u64 event_id;
+};
+
+struct mausb_delete_ma_dev {
+	u64 event_id;
+	u16 device_id;
+};
+
+/* TODO split mgmt_event to generic send mgmt req and specific requests */
+struct mausb_mgmt_event {
+	union {
+		struct mausb_devhandle		dev_handle;
+		struct mausb_ephandle		ep_handle;
+		struct mausb_epactivate		ep_activate;
+		struct mausb_epinactivate	ep_inactivate;
+		struct mausb_epreset		ep_reset;
+		struct mausb_epdelete		ep_delete;
+		struct mausb_modifyep0		modify_ep0;
+		struct mausb_setusbdevaddress	set_usb_dev_address;
+		struct mausb_updatedev		update_dev;
+		struct mausb_usbdevreset	usb_dev_reset;
+		struct mausb_usbdevdisconnect	usb_dev_disconnect;
+		struct mausb_canceltransfer	cancel_transfer;
+		struct mausb_mgmt_hdr		mgmt_hdr;
+		struct mausb_mgmt_req_timedout	mgmt_req_timedout;
+		struct mausb_delete_ma_dev	delete_ma_dev;
+	};
+};
+
+struct mausb_data_event {
+	uintptr_t urb;
+	uintptr_t recv_buf;
+	u32 iterator_seek_delta;
+	u32 transfer_size;
+	u32 rem_transfer_size;
+	u32 transfer_flags;
+	u32 isoch_seg_num;
+	u32 req_id;
+	u32 payload_size;
+	s32 status;
+
+	__aligned(4) char hdr[MAUSB_TRANSFER_HDR_SIZE];
+	__aligned(4) char hdr_ack[MAUSB_TRANSFER_HDR_SIZE];
+
+	u16 device_id;
+	u16 ep_handle;
+	u16 packet_size;
+	u8  setup_packet;
+	u8  direction;
+	u8  transfer_type;
+	u8  first_control_packet;
+	u8  transfer_eot;
+	u8  mausb_address;
+	u8  mausb_ssid;
+};
+
+struct mausb_port_changed_event {
+	u8 port;
+	u8 dev_type;
+	u8 dev_speed;
+	u8 lse;
+};
+
+struct mausb_event {
+	union {
+		struct mausb_mgmt_event		mgmt;
+		struct mausb_data_event		data;
+		struct mausb_port_changed_event port_changed;
+	};
+	s32 status;
+	u8 type;
+	u8 madev_addr;
+};
+
+struct mausb_events_notification {
+	u16 num_of_events;
+	u16 num_of_completed_events;
+	u8  madev_addr;
+};
+
+#endif /* __MAUSB_MAUSB_EVENT_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (4 preceding siblings ...)
  2020-04-25  9:19     ` [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-25  9:19     ` [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
                       ` (3 subsequent siblings)
  9 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemeneted connection between PAL and Link layers and set up
environment for exchanging PAL-to-PAL messages.

Within this patch, driver's sysfs parameters have been created
with intention to configure remote connection parameters.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/hpal.c       | 522 ++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal.h       |   6 +
 drivers/usb/mausb_host/mausb_core.c | 103 +++++-
 3 files changed, 630 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index e10b98002afd..74bebc03d7e6 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -13,10 +13,15 @@
 #include "hcd.h"
 #include "utils.h"
 
+#define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
+
 struct mss mss;
 
+static int mausb_start_connection_timer(struct mausb_device *dev);
 static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
 				void *data);
+static void mausb_signal_empty_mss(void);
+static void mausb_remove_madev_from_list(u8 madev_addr);
 static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
 static int mausb_start_heartbeat_timer(void);
 
@@ -177,6 +182,55 @@ static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx,
 	return true;
 }
 
+static void mausb_complete_urbs_from_tree(void)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+	struct urb	     *current_urb = NULL;
+	struct rb_node	     *current_node = NULL;
+	unsigned long flags;
+	int status = 0;
+	int ret;
+
+	mausb_pr_debug("Completing all urbs from tree");
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	while ((current_node = rb_first(&mhcd->mausb_urbs))) {
+		urb_ctx = rb_entry(current_node, struct mausb_urb_ctx, rb_node);
+
+		current_urb = urb_ctx->urb;
+		mausb_delete_urb_ctx_from_tree(urb_ctx);
+		mausb_uninit_data_iterator(&urb_ctx->iterator);
+		kfree(urb_ctx);
+
+		ret = usb_hcd_check_unlink_urb(current_urb->hcpriv,
+					       current_urb, status);
+		if (ret == -EIDRM)
+			mausb_pr_warn("Urb=%p is already unlinked",
+				      current_urb);
+		else
+			usb_hcd_unlink_urb_from_ep(current_urb->hcpriv,
+						   current_urb);
+
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+
+		/* Prepare urb for completion */
+		mausb_pr_debug("Completing urb=%p", current_urb);
+
+		current_urb->status	   = -EPROTO;
+		current_urb->actual_length = 0;
+		atomic_dec(&current_urb->use_count);
+		usb_hcd_giveback_urb(current_urb->hcpriv, current_urb,
+				     current_urb->status);
+
+		spin_lock_irqsave(&mhcd->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	mausb_pr_debug("Completed all urbs from tree");
+}
+
 /*After this function call only valid thing to do with urb is to give it back*/
 struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
 							    int status)
@@ -284,6 +338,161 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev,
 		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+static void mausb_socket_disconnect_event(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						socket_disconnect_work);
+	struct mausb_event event;
+
+	mausb_pr_info("madev_addr=%d", dev->madev_addr);
+
+	mausb_ip_disconnect(dev->ctrl_channel);
+	mausb_destroy_ip_ctx(dev->ctrl_channel);
+	dev->ctrl_channel = NULL;
+
+	mausb_ip_disconnect(dev->bulk_channel);
+	mausb_destroy_ip_ctx(dev->bulk_channel);
+	dev->bulk_channel = NULL;
+
+	mausb_ip_disconnect(dev->isoch_channel);
+	mausb_destroy_ip_ctx(dev->isoch_channel);
+	dev->isoch_channel = NULL;
+
+	if (dev->mgmt_channel) {
+		memset(&event, 0, sizeof(event));
+		event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
+		event.data.device_id = dev->id;
+
+		mausb_pr_info("Releasing MAUSB device ref");
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+	}
+
+	mausb_ip_disconnect(dev->mgmt_channel);
+	mausb_destroy_ip_ctx(dev->mgmt_channel);
+	dev->mgmt_channel = NULL;
+
+	memset(dev->channel_map, 0, sizeof(dev->channel_map));
+}
+
+static void mausb_disconnect_ma_dev(struct mausb_device *dev)
+{
+	mausb_pr_info("Disconnecting MAUSB device madev_addr=%d",
+		      dev->madev_addr);
+
+	if (!dev->dev_connected) {
+		mausb_pr_warn("MAUSB device is not connected");
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return;
+	}
+	mausb_hcd_disconnect(dev->port_number, dev->dev_type, dev->dev_speed);
+
+	if (dev->dev_type == USB30HUB)
+		mausb_hcd_disconnect(dev->port_number, USB20HUB, HIGH_SPEED);
+}
+
+static void mausb_hcd_disconnect_event(struct work_struct *work)
+{
+	struct mausb_device *ma_dev = container_of(work, struct mausb_device,
+						   hcd_disconnect_work);
+
+	mausb_disconnect_ma_dev(ma_dev);
+}
+
+static void mausb_delete_madev(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						madev_delete_work);
+	struct mausb_event	event;
+	struct completion	completion;
+	struct completion	*user_event;
+	struct mausb_completion mausb_completion;
+	long status;
+	unsigned long timeout = msecs_to_jiffies(MAUSB_DELETE_MADEV_TIMEOUT_MS);
+
+	mausb_pr_info("Deleting MAUSB device madev_addr=%d", dev->madev_addr);
+
+	del_timer_sync(&dev->connection_timer);
+
+	/* Client IS responsive */
+	if (!atomic_read(&dev->unresponsive_client)) {
+		memset(&event, 0, sizeof(event));
+		event.type = MAUSB_EVENT_TYPE_DELETE_MA_DEV;
+		event.mgmt.delete_ma_dev.device_id = dev->id;
+		event.mgmt.delete_ma_dev.event_id  = mausb_event_id(dev);
+
+		init_completion(&completion);
+		mausb_completion.completion_event = &completion;
+		mausb_completion.event_id = event.mgmt.delete_ma_dev.event_id;
+		mausb_completion.mausb_event = &event;
+
+		mausb_insert_event(dev, &mausb_completion);
+
+		mausb_pr_debug("Deleting MAUSB device...");
+
+		status = wait_for_completion_interruptible_timeout(&completion,
+								   timeout);
+
+		mausb_pr_debug("Deleting MAUSB device event finished with %ld",
+			       status);
+
+		mausb_remove_event(dev, &mausb_completion);
+
+		user_event = &dev->user_finished_event;
+
+		status = wait_for_completion_interruptible_timeout(user_event,
+								   timeout);
+		mausb_pr_info("User event finished with %ld", status);
+	}
+
+	flush_workqueue(dev->workq);
+	destroy_workqueue(dev->workq);
+
+	mausb_clear_hcd_madev(dev->port_number);
+
+	mausb_remove_madev_from_list(dev->madev_addr);
+
+	put_net(dev->net_ns);
+
+	kfree(dev);
+	mausb_signal_empty_mss();
+
+	mausb_pr_info("MAUSB device deleted. Version=%s", MAUSB_DRIVER_VERSION);
+}
+
+static void mausb_ping_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						ping_work);
+
+	if (mausb_start_connection_timer(dev) < 0) {
+		mausb_pr_err("Device disconnecting due to session timeout madev_addr=%d",
+			     dev->madev_addr);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return;
+	}
+}
+
+static void mausb_heartbeat_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						heartbeat_work);
+
+	mausb_pr_err("Device disconnecting - app is unresponsive");
+	atomic_set(&dev->unresponsive_client, 1);
+	mausb_complete_urbs_from_tree();
+	queue_work(dev->workq, &dev->socket_disconnect_work);
+	queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
+
+static void mausb_connection_timer_func(struct timer_list *timer)
+{
+	struct mausb_device *dev = container_of(timer, struct mausb_device,
+						connection_timer);
+
+	queue_work(dev->workq, &dev->ping_work);
+}
+
 static void mausb_heartbeat_timer_func(struct timer_list *timer)
 {
 	unsigned long flags = 0;
@@ -308,6 +517,99 @@ static void mausb_heartbeat_timer_func(struct timer_list *timer)
 	}
 }
 
+static struct mausb_device *
+mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
+		   int *status)
+{
+	struct mausb_device *dev;
+	unsigned long flags = 0;
+	char workq_name[16];
+	struct workqueue_struct *workq;
+
+	memset(workq_name, 0, sizeof(workq_name));
+	sprintf(workq_name, "%x", madev_address);
+	strcat(workq_name, "_madev_workq");
+
+	mausb_pr_debug("madev_workq_name = %s", workq_name);
+
+	workq = alloc_ordered_workqueue(workq_name, WQ_MEM_RECLAIM);
+	if (!workq) {
+		mausb_pr_alert("Could not allocate workqueue!");
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	if (mss.deinit_in_progress) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		mausb_pr_alert("Device creating failed - mss deinit in progress");
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -ESHUTDOWN;
+		return NULL;
+	}
+
+	dev = mausb_get_dev_from_addr_unsafe(madev_address);
+	if (dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		mausb_pr_debug("MAUSB device already connected, madev_address=%x",
+			       madev_address);
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -EEXIST;
+		return NULL;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		mausb_pr_alert("Could not allocate MAUSB device!");
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	mausb_pr_info("Create MAUSB device. Version=%s", MAUSB_DRIVER_VERSION);
+
+	dev->workq = workq;
+
+	INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event);
+	INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event);
+	INIT_WORK(&dev->madev_delete_work, mausb_delete_madev);
+	INIT_WORK(&dev->ping_work, mausb_ping_work);
+	INIT_WORK(&dev->heartbeat_work, mausb_heartbeat_work);
+
+	kref_init(&dev->refcount);
+
+	dev->event_id = 0;
+	spin_lock_init(&dev->event_id_lock);
+
+	INIT_LIST_HEAD(&dev->completion_events);
+	spin_lock_init(&dev->completion_events_lock);
+	spin_lock_init(&dev->num_of_user_events_lock);
+	spin_lock_init(&dev->connection_timer_lock);
+
+	init_completion(&dev->user_finished_event);
+	atomic_set(&dev->unresponsive_client, 0);
+
+	timer_setup(&dev->connection_timer, mausb_connection_timer_func, 0);
+
+	dev->dev_addr = dev_addr;
+	dev->madev_addr = madev_address;
+	dev->net_ns = get_net(current->nsproxy->net_ns);
+
+	list_add_tail(&dev->list_entry, &mss.madev_list);
+
+	reinit_completion(&mss.empty);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return dev;
+}
+
 void mausb_release_ma_dev_async(struct kref *kref)
 {
 	struct mausb_device *dev = container_of(kref, struct mausb_device,
@@ -318,6 +620,45 @@ void mausb_release_ma_dev_async(struct kref *kref)
 	schedule_work(&dev->madev_delete_work);
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
+				  u8 madev_address)
+{
+	int error = 0;
+	struct mausb_device *dev;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(madev_address);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	if (dev) {
+		mausb_pr_debug("MAUSB device already connected, madev_address=%x",
+			       madev_address);
+		return -EEXIST;
+	}
+
+	dev = mausb_create_madev(dev_addr, madev_address, &error);
+
+	if (!dev)
+		return error;
+
+	mausb_pr_info("New MAUSB device created madev_addr=%d", madev_address);
+
+	error = mausb_init_ip_ctx(&dev->mgmt_channel, dev->net_ns,
+				  dev->dev_addr.ip.address,
+				  dev->dev_addr.ip.port.management, dev,
+				  mausb_ip_callback, MAUSB_MGMT_CHANNEL);
+	if (error) {
+		mausb_pr_err("Mgmt ip context init failed: error=%d", error);
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return error;
+	}
+
+	mausb_ip_connect_async(dev->mgmt_channel);
+
+	return 0;
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 				  u16 num_of_completed)
 {
@@ -418,6 +759,26 @@ int mausb_signal_event(struct mausb_device *dev,
 	return -ETIMEDOUT;
 }
 
+static int mausb_start_connection_timer(struct mausb_device *dev)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&dev->connection_timer_lock, flags);
+
+	if (++dev->receive_failures_num > MAUSB_MAX_RECEIVE_FAILURES) {
+		mausb_pr_err("Missed more than %d ping responses",
+			     MAUSB_MAX_RECEIVE_FAILURES);
+		spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+	spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+
+	return 0;
+}
+
 void mausb_reset_connection_timer(struct mausb_device *dev)
 {
 	unsigned long flags = 0;
@@ -661,6 +1022,36 @@ struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr)
 	return NULL;
 }
 
+static void mausb_remove_madev_from_list(u8 madev_addr)
+{
+	unsigned long flags = 0;
+	struct mausb_device *ma_dev, *tmp = NULL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	list_for_each_entry_safe(ma_dev, tmp, &mss.madev_list, list_entry) {
+		if (ma_dev->madev_addr == madev_addr) {
+			list_del(&ma_dev->list_entry);
+			break;
+		}
+	}
+
+	if (list_empty(&mss.madev_list))
+		reinit_completion(&mss.rings_events.mausb_ring_has_events);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_signal_empty_mss(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (list_empty(&mss.madev_list))
+		complete(&mss.empty);
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
 static inline
 struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
 					    enum mausb_channel channel)
@@ -814,6 +1205,137 @@ void mausb_cleanup_chunks_list(struct list_head *chunks_list)
 	}
 }
 
+static void mausb_init_ip_ctx_helper(struct mausb_device *dev,
+				     struct mausb_ip_ctx **ip_ctx,
+				     u16 port,
+				     enum mausb_channel channel)
+{
+	int status = mausb_init_ip_ctx(ip_ctx, dev->net_ns,
+				       dev->dev_addr.ip.address, port, dev,
+				       mausb_ip_callback, channel);
+	if (status < 0) {
+		mausb_pr_err("Init ip context failed with error=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	dev->channel_map[channel] = *ip_ctx;
+	mausb_ip_connect_async(*ip_ctx);
+}
+
+static void mausb_connect_callback(struct mausb_device *dev,
+				   enum mausb_channel channel, int status)
+{
+	struct mausb_device_address *dev_addr = &dev->dev_addr;
+
+	mausb_pr_info("Connect callback for channel=%d with status=%d",
+		      channel, status);
+
+	if (status < 0) {
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	if (channel == MAUSB_MGMT_CHANNEL) {
+		if (dev_addr->ip.port.control == 0) {
+			dev->channel_map[MAUSB_CTRL_CHANNEL] =
+				dev->mgmt_channel;
+			channel = MAUSB_CTRL_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->ctrl_channel,
+						 dev_addr->ip.port.control,
+						 MAUSB_CTRL_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_CTRL_CHANNEL) {
+		if (dev_addr->ip.port.bulk == 0) {
+			dev->channel_map[MAUSB_BULK_CHANNEL] =
+				dev->channel_map[MAUSB_CTRL_CHANNEL];
+			channel = MAUSB_BULK_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->bulk_channel,
+						 dev_addr->ip.port.bulk,
+						 MAUSB_BULK_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_BULK_CHANNEL) {
+		if (dev_addr->ip.port.isochronous == 0) {
+			/* if there is no isoch port use tcp for it */
+			dev->channel_map[MAUSB_ISOCH_CHANNEL] =
+				dev->channel_map[MAUSB_BULK_CHANNEL];
+			channel = MAUSB_ISOCH_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->isoch_channel,
+						 dev_addr->ip.port.isochronous,
+						 MAUSB_ISOCH_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_ISOCH_CHANNEL) {
+		dev->channel_map[MAUSB_INTR_CHANNEL] =
+				dev->channel_map[MAUSB_CTRL_CHANNEL];
+	}
+}
+
+static void mausb_handle_connect_event(struct mausb_device *dev,
+				       enum mausb_channel channel, int status,
+				       void *data)
+{
+	mausb_connect_callback(dev, channel, status);
+}
+
+static void mausb_handle_receive_event(struct mausb_device *dev,
+				       enum mausb_channel channel, int status,
+				       void *data)
+{
+	struct mausb_event event;
+
+	event.madev_addr = dev->madev_addr;
+
+	if (status <= 0) {
+		mausb_pr_err("Receive event error status=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return;
+	}
+
+	mausb_reset_connection_timer(dev);
+}
+
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+		       enum mausb_link_action action, int status, void *data)
+{
+	struct mausb_device *dev = (struct mausb_device *)ctx;
+
+	switch (action) {
+	case MAUSB_LINK_CONNECT:
+		mausb_handle_connect_event(dev, channel, status, data);
+		break;
+	case MAUSB_LINK_SEND:
+		/*
+		 * Currently there is nothing to do, as send operation is
+		 * synchronous
+		 */
+		break;
+	case MAUSB_LINK_RECV:
+		mausb_handle_receive_event(dev, channel, status, data);
+		break;
+	case MAUSB_LINK_DISCONNECT:
+		/*
+		 * Currently there is nothing to do, as disconnect operation is
+		 * synchronous
+		 */
+		break;
+	default:
+		mausb_pr_warn("Unknown network action");
+	}
+}
+
 static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
 				     u32 byte_num,
 				     struct list_head *data_chunks_list,
diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h
index f184bbc07783..a04ed120ba5e 100644
--- a/drivers/usb/mausb_host/hpal.h
+++ b/drivers/usb/mausb_host/hpal.h
@@ -131,6 +131,8 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
 	return val;
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address device_address,
+				  u8 madev_address);
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request);
 int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
@@ -158,6 +160,7 @@ static inline void mausb_remove_event(struct mausb_device *dev,
 }
 
 void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_on_madev_connected(struct mausb_device *dev);
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
 void mausb_complete_urb(struct mausb_event *event);
 void mausb_reset_connection_timer(struct mausb_device *dev);
@@ -240,6 +243,9 @@ enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type)
 	return transfer_type >> 3;
 }
 
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+		       enum mausb_link_action action, int status, void *data);
+
 struct mausb_data_iter {
 	u32 length;
 
diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
index 8730590126ea..101afd0b9deb 100644
--- a/drivers/usb/mausb_host/mausb_core.c
+++ b/drivers/usb/mausb_host/mausb_core.c
@@ -13,41 +13,122 @@
 
 #include "hcd.h"
 #include "hpal.h"
+#include "mausb_address.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("DisplayLink (UK) Ltd.");
 MODULE_VERSION(MAUSB_DRIVER_VERSION);
 
+static struct mausb_device_address	device_address;
+static int				mausb_device_disconnect_param;
+static u16				madev_addr;
+static u8				mausb_client_connect_param;
+static u8				mausb_client_disconnect_param;
+
 static int mausb_client_connect(const char *value,
 				const struct kernel_param *kp)
 {
+	unsigned long flags = 0;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
+	spin_lock_irqsave(&mss.lock, flags);
+	if (mss.client_connected) {
+		mausb_pr_err("MA-USB client is already connected");
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -EEXIST;
+	}
+	/* Save heartbeat client information */
+	mss.client_connected = true;
+	mss.missed_heartbeats = 0;
+	reinit_completion(&mss.client_stopped);
+	spin_unlock_irqrestore(&mss.lock, flags);
+	/* Start hearbeat timer */
+	mod_timer(&mss.heartbeat_timer,
+		  jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
 	return 0;
 }
 
 static int mausb_client_disconnect(const char *value,
 				   const struct kernel_param *kp)
 {
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
+	spin_lock_irqsave(&mss.lock, flags);
+	if (!mss.client_connected) {
+		mausb_pr_err("MA-USB client is not connected");
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -ENODEV;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	/* Stop heartbeat timer */
+	del_timer_sync(&mss.heartbeat_timer);
+
+	/* Clear heartbeat client information */
+	spin_lock_irqsave(&mss.lock, flags);
+	mss.client_connected = false;
+	mss.missed_heartbeats = 0;
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x",
+			       dev->madev_addr);
+		queue_work(dev->workq, &dev->heartbeat_work);
+	}
+	complete(&mss.client_stopped);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
 	return 0;
 }
 
 static int mausb_device_connect(const char *value,
 				const struct kernel_param *kp)
 {
+	int status = 0;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
-	return 0;
+	if (strlen(value) <= INET6_ADDRSTRLEN) {
+		strcpy(device_address.ip.address, value);
+		mausb_pr_info("Processing '%s' address",
+			      device_address.ip.address);
+	} else {
+		mausb_pr_err("Invalid IP format");
+		return 0;
+	}
+	status = mausb_initiate_dev_connection(device_address, madev_addr);
+	memset(&device_address, 0, sizeof(device_address));
+
+	return status;
 }
 
 static int mausb_device_disconnect(const char *value,
 				   const struct kernel_param *kp)
 {
+	u8 dev_address = 0;
+	int status = 0;
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
 	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);
 
+	status = kstrtou8(value, 0, &dev_address);
+	if (status < 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	dev = mausb_get_dev_from_addr_unsafe(dev_address);
+	if (dev)
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
 	return 0;
 }
 
@@ -67,6 +148,26 @@ static const struct kernel_param_ops mausb_client_disconnect_ops = {
 	.set = mausb_client_disconnect
 };
 
+module_param_named(mgmt, device_address.ip.port.management, ushort, 0664);
+MODULE_PARM_DESC(mgmt, "MA-USB management port");
+module_param_named(ctrl, device_address.ip.port.control, ushort, 0664);
+MODULE_PARM_DESC(ctrl, "MA-USB control port");
+module_param_named(bulk, device_address.ip.port.bulk, ushort, 0664);
+MODULE_PARM_DESC(bulk, "MA-USB bulk port");
+module_param_named(isoch, device_address.ip.port.isochronous, ushort, 0664);
+MODULE_PARM_DESC(isoch, "MA-USB isochronous port");
+module_param_named(madev_addr, madev_addr, ushort, 0664);
+MODULE_PARM_DESC(madev_addr, "MA-USB device address");
+
+module_param_cb(client_connect, &mausb_client_connect_ops,
+		&mausb_client_connect_param, 0664);
+module_param_cb(client_disconnect, &mausb_client_disconnect_ops,
+		&mausb_client_disconnect_param, 0664);
+module_param_cb(ip, &mausb_device_connect_ops,
+		device_address.ip.address, 0664);
+module_param_cb(disconnect, &mausb_device_disconnect_ops,
+		&mausb_device_disconnect_param, 0664);
+
 static int mausb_host_init(void)
 {
 	int status;
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (5 preceding siblings ...)
  2020-04-25  9:19     ` [PATCH v5 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-28 11:08       ` Greg KH
  2020-04-25  9:19     ` [PATCH v5 8/8] usb: mausb_host: Process MA-USB data packets vladimir.stankovic
                       ` (2 subsequent siblings)
  9 siblings, 1 reply; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented MA-USB management messages processing and communication
with user-space driver via mapped memory.

MA USB PAL events are being enqueued into ring buffer from which
data is being send/received to/from user-space.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile              |   1 +
 drivers/usb/mausb_host/hcd.c                 | 245 +++++++-
 drivers/usb/mausb_host/hpal.c                | 449 +++++++++++++-
 drivers/usb/mausb_host/hpal.h                |  44 ++
 drivers/usb/mausb_host/hpal_events.c         | 611 +++++++++++++++++++
 drivers/usb/mausb_host/hpal_events.h         |  85 +++
 drivers/usb/mausb_host/mausb_driver_status.h |  17 +
 drivers/usb/mausb_host/utils.c               | 273 +++++++++
 drivers/usb/mausb_host/utils.h               |   5 +
 9 files changed, 1720 insertions(+), 10 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal_events.c
 create mode 100644 drivers/usb/mausb_host/hpal_events.h
 create mode 100644 drivers/usb/mausb_host/mausb_driver_status.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index 829314b15cbb..fd2a36a04ad6 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -11,5 +11,6 @@ mausb_host-y += utils.o
 mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
+mausb_host-y += hpal_events.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hcd.c b/drivers/usb/mausb_host/hcd.c
index 5967c30d49a3..3e14add3e309 100644
--- a/drivers/usb/mausb_host/hcd.c
+++ b/drivers/usb/mausb_host/hcd.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 
 #include "hpal.h"
+#include "hpal_events.h"
 #include "utils.h"
 
 static int mausb_open(struct inode *inode, struct file *file);
@@ -1073,6 +1074,18 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 	}
 
 	if (ep_ctx) {
+		status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
+							  ep_ctx->ep_handle);
+
+		mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d",
+			      ep_ctx->ep_handle, dev_handle, status);
+
+		status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
+						      ep_ctx->ep_handle);
+
+		if (status < 0)
+			mausb_pr_warn("ep_handle_del request failed for ep0: ep_handle=%#x, dev_handle=%#x",
+				      ep_ctx->ep_handle, dev_handle);
 		dev->ep0.hcpriv = NULL;
 		kfree(ep_ctx);
 
@@ -1080,6 +1093,14 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle);
 	}
 
+	if (dev_handle != DEV_HANDLE_NOT_ASSIGNED) {
+		status = mausb_usbdevdisconnect_event_to_user(ma_dev,
+							      dev_handle);
+		if (status < 0)
+			mausb_pr_warn("usb_dev_disconnect req failed for dev_handle=%#x",
+				      dev_handle);
+	}
+
 free_dev:
 	if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
 		mausb_pr_info("All usb devices destroyed - proceed with disconnecting");
@@ -1094,6 +1115,21 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_clear_hcd_madev(port_number);
 }
 
+static int mausb_device_assign_address(struct mausb_device *dev,
+				       struct mausb_usb_device_ctx *usb_dev_ctx)
+{
+	int status =
+		mausb_setusbdevaddress_event_to_user(dev,
+						     usb_dev_ctx->dev_handle,
+						     RESPONSE_TIMEOUT);
+
+	mausb_pr_info("dev_handle=%#x, status=%d", usb_dev_ctx->dev_handle,
+		      status);
+	usb_dev_ctx->addressed = (status == 0);
+
+	return status;
+}
+
 static struct mausb_usb_device_ctx *
 mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
 		       struct mausb_device *ma_dev, u16 port_number,
@@ -1176,6 +1212,12 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 			return status;
 	}
 
+	if (!usb_device_ctx->addressed) {
+		status = mausb_device_assign_address(ma_dev, usb_device_ctx);
+		if (status < 0)
+			return status;
+	}
+
 	endpoint_ctx = dev->ep0.hcpriv;
 	if (!endpoint_ctx) {
 		mausb_pr_err("endpoint_ctx is NULL: dev_handle=%#x",
@@ -1183,7 +1225,19 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	return 0;
+	if (dev->speed < USB_SPEED_SUPER && endpoint_ctx->ep_handle != 0)
+		return 0;
+
+	status = mausb_modifyep0_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       &endpoint_ctx->ep_handle,
+					       dev->ep0.desc.wMaxPacketSize);
+
+	mausb_pr_info("modify ep0 response, ep_handle=%#x, dev_handle=%#x, status=%d",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		      status);
+
+	return status;
 }
 
 static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
@@ -1236,10 +1290,32 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
 						    &endpoint->desc,
 						    &endpoint->ss_ep_comp);
+		status = mausb_ephandle_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      sizeof(descriptor_ss),
+						      &descriptor_ss,
+						      &endpoint_ctx->ep_handle);
+
 	} else {
 		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+		status = mausb_ephandle_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      sizeof(descriptor),
+						      &descriptor,
+						      &endpoint_ctx->ep_handle);
 	}
 
+	if (status < 0) {
+		mausb_pr_err("ep_handle_request failed dev_handle=%#x",
+			     usb_dev_ctx->dev_handle);
+		endpoint->hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return status;
+	}
+
+	mausb_pr_info("Endpoint added ep_handle=%#x, dev_handle=%#x",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
 	return 0;
 }
 
@@ -1248,6 +1324,7 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	u8	port_number;
 	int	status;
+	int	retries	    = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
@@ -1282,9 +1359,49 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return -ENODEV;
 	}
 
+	mausb_pr_info("Start dropping ep_handle=%#x, dev_handle=%#x",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		mausb_pr_err("Client is not responsive anymore - drop endpoint immediately");
+		endpoint->hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return -ESHUTDOWN;
+	}
+
+	status = mausb_epinactivate_event_to_user(ma_dev,
+						  usb_dev_ctx->dev_handle,
+						  endpoint_ctx->ep_handle);
+
+	mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		      status);
+
+	while (true) {
+		status = mausb_epdelete_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      endpoint_ctx->ep_handle);
+
+		mausb_pr_info("ep_handle_delete_request, ep_handle=%#x, dev_handle=%#x, status=%d",
+			      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+			      status);
+
+		if (status == -EBUSY) {
+			if (++retries < MAUSB_BUSY_RETRIES_COUNT)
+				usleep_range(10000, 10001);
+			else
+				return -EBUSY;
+		} else {
+			break;
+		}
+	}
+
+	mausb_pr_info("Endpoint dropped ep_handle=%#x, dev_handle=%#x",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
 	endpoint->hcpriv = NULL;
 	kfree(endpoint_ctx);
-	return 0;
+	return status;
 }
 
 static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
@@ -1337,6 +1454,20 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
 		return -EINVAL;
 	}
 
+	status = mausb_usbdevhandle_event_to_user(ma_dev, (u8)dev_speed,
+						  dev->route, hub_dev_handle,
+						  parent_hs_hub_dev_handle,
+						  parent_hs_hub_port, 0,
+						  ma_dev->lse,
+						  &usb_device_ctx->dev_handle);
+
+	mausb_pr_info("mausb_usbdevhandle_event status=%d", status);
+
+	if (status < 0)
+		return status;
+
+	mausb_pr_info("Get ref dev_handle=%#x", usb_device_ctx->dev_handle);
+
 	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
 	if (!endpoint_ctx)
 		return -ENOMEM;
@@ -1348,6 +1479,22 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
 
 	mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
 
+	status = mausb_ephandle_event_to_user(ma_dev,
+					      usb_device_ctx->dev_handle,
+					      sizeof(descriptor),
+					      &descriptor,
+					      &endpoint_ctx->ep_handle);
+
+	mausb_pr_info("mausb_ephandle_event ep_handle=%#x, dev_handle=%#x, status=%d",
+		      endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		      status);
+
+	if (status < 0) {
+		dev->ep0.hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return status;
+	}
+
 	return 0;
 }
 
@@ -1385,9 +1532,18 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 			return status;
 	}
 
+	mausb_pr_info("Device assigned and addressed usb_device_ctx=%p",
+		      usb_device_ctx);
+
 	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
 		return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
 						      usb_device_ctx);
+
+	if (!usb_device_ctx->addressed)
+		return mausb_device_assign_address(ma_dev, usb_device_ctx);
+
+	mausb_pr_info("Device assigned and addressed dev_handle=%#x",
+		      usb_device_ctx->dev_handle);
 	return 0;
 }
 
@@ -1433,7 +1589,15 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -ENODEV;
 	}
 
-	return 0;
+	status = mausb_updatedev_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       0, 0, 0, 0, 0, 0,
+					       &dev->descriptor);
+
+	mausb_pr_info("Finished dev_handle=%#x, status=%d",
+		      usb_device_ctx->dev_handle, status);
+
+	return status;
 }
 
 static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
@@ -1443,8 +1607,10 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 	u8	port_number;
 	unsigned long flags;
 	u16 max_exit_latency = 0;
+	u8  number_of_ports = (u8)dev->maxchild;
 	u8  mtt = 0;
 	u8  ttt = 0;
+	u8  integrated_hub_latency = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
@@ -1484,7 +1650,17 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 	else if (dev->usb3_lpm_u2_enabled)
 		max_exit_latency = (u16)dev->u2_params.mel;
 
-	return 0;
+	status = mausb_updatedev_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       max_exit_latency, 1,
+					       number_of_ports, mtt, ttt,
+					       integrated_hub_latency,
+					       &dev->descriptor);
+
+	mausb_pr_info("Finished dev_handle=%#x, status=%d",
+		      usb_device_ctx->dev_handle, status);
+
+	return status;
 }
 
 static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
@@ -1520,6 +1696,8 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct usb_device	    *dev;
 	struct mausb_endpoint_ctx   *ep_ctx;
+	struct ma_usb_ephandlereq_desc_ss  descriptor_ss;
+	struct ma_usb_ephandlereq_desc_std descriptor;
 
 	ep_ctx = endpoint->hcpriv;
 	if (!ep_ctx) {
@@ -1554,6 +1732,15 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	is_out = usb_endpoint_dir_out(&endpoint->desc);
 	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
 
+	status = mausb_epreset_event_to_user(ma_dev, dev_handle,
+					     ep_ctx->ep_handle, tsp);
+
+	mausb_pr_info("ep_reset status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
 	if (status != EUCLEAN) {
 		if (!tsp) {
 			usb_settoggle(dev, epnum, is_out, 0U);
@@ -1561,12 +1748,52 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 				usb_settoggle(dev, epnum, !is_out, 0U);
 		}
 
+		status = mausb_epactivate_event_to_user(ma_dev, dev_handle,
+							ep_ctx->ep_handle);
+
+		mausb_pr_err("ep_activate failed, status=%d, ep_handle=%#x, dev_handle=%#x",
+			     status, ep_ctx->ep_handle, dev_handle);
+
 		return;
 	}
 
 	if (tsp)
 		return;
 
+	status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
+						  ep_ctx->ep_handle);
+
+	mausb_pr_info("ep_inactivate status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
+	status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
+					      ep_ctx->ep_handle);
+
+	mausb_pr_info("ep_handle_delete status=%d, ep_handle=%#x, dev_handle=%#x",
+		      status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
+	if (dev->speed >= USB_SPEED_SUPER) {
+		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
+						    &endpoint->desc,
+						    &endpoint->ss_ep_comp);
+		status = mausb_ephandle_event_to_user(ma_dev, dev_handle,
+						      sizeof(descriptor_ss),
+						      &descriptor_ss,
+						      &ep_ctx->ep_handle);
+	} else {
+		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+		status = mausb_ephandle_event_to_user(ma_dev, dev_handle,
+						      sizeof(descriptor),
+						      &descriptor,
+						      &ep_ctx->ep_handle);
+	}
+
 	mausb_pr_info("ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x",
 		      status, ep_ctx->ep_handle, dev_handle);
 }
@@ -1608,7 +1835,15 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 
 	dev_handle = usb_device_ctx->dev_handle;
 
-	return 0;
+	status = mausb_usbdevreset_event_to_user(ma_dev, dev_handle);
+
+	mausb_pr_info("usb_dev_reset dev_handle=%#x, status=%d",
+		      dev_handle, status);
+
+	if (status == 0)
+		usb_device_ctx->addressed = false;
+
+	return status;
 }
 
 void mausb_clear_hcd_madev(u16 port_number)
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index 74bebc03d7e6..bf678f2707b9 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -11,6 +11,7 @@
 #include <linux/uio.h>
 
 #include "hcd.h"
+#include "hpal_events.h"
 #include "utils.h"
 
 #define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
@@ -278,6 +279,31 @@ void mausb_release_event_resources(struct mausb_event *event)
 	kfree(receive_buffer);
 }
 
+static void mausb_iterator_reset(struct mausb_device *dev,
+				 struct mausb_event *event)
+{
+	struct urb	     *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+
+	urb_ctx = mausb_find_urb_in_tree(urb);
+
+	if (urb_ctx)
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+}
+
+static void mausb_iterator_seek(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	struct urb	     *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+
+	urb_ctx = mausb_find_urb_in_tree(urb);
+
+	if (urb_ctx)
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 event->data.iterator_seek_delta);
+}
+
 void mausb_complete_urb(struct mausb_event *event)
 {
 	struct urb *urb = (struct urb *)event->data.urb;
@@ -291,6 +317,46 @@ void mausb_complete_urb(struct mausb_event *event)
 			       event->status);
 }
 
+static void mausb_delete_ma_dev(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	mausb_signal_event(dev, event, event->mgmt.delete_ma_dev.event_id);
+}
+
+static void mausb_process_user_finished(struct mausb_device *dev,
+					struct mausb_event *event)
+{
+	complete(&dev->user_finished_event);
+}
+
+static int mausb_send_mgmt_msg(struct mausb_device *dev,
+			       struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper wrapper;
+	struct kvec kvec;
+	struct ma_usb_hdr_common *hdr;
+	int status;
+
+	hdr = (struct ma_usb_hdr_common *)event->mgmt.mgmt_hdr.hdr;
+
+	mausb_pr_info("event=%d, type=%d", event->type, hdr->type);
+
+	kvec.iov_base	 = hdr;
+	kvec.iov_len	 = hdr->length;
+	wrapper.kvec	 = &kvec;
+	wrapper.kvec_num = 1;
+	wrapper.length	 = hdr->length;
+
+	status = mausb_ip_send(dev->mgmt_channel, &wrapper);
+	if (status < 0) {
+		mausb_pr_err("Send failed. Disconnecting... status=%d", status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	return status;
+}
+
 static int mausb_get_first_free_port_number(u16 *port_number)
 {
 	(*port_number) = 0;
@@ -338,11 +404,142 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev,
 		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+static void mausb_complete_timeout_event(struct mausb_device *dev,
+					 struct mausb_event *event)
+{
+	mausb_pr_debug("Event type=%d, event_id=%llu", event->type,
+		       event->mgmt.mgmt_req_timedout.event_id);
+	mausb_signal_event(dev, event, event->mgmt.mgmt_req_timedout.event_id);
+}
+
+static void mausb_process_event(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	mausb_pr_debug("Event type=%d", event->type);
+
+	switch (event->type) {
+	case MAUSB_EVENT_TYPE_USB_DEV_HANDLE:
+		mausb_usbdevhandle_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE:
+		mausb_ephandle_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE:
+		mausb_epactivate_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE:
+		mausb_epinactivate_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_RESET:
+		mausb_epreset_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_DELETE:
+		mausb_epdelete_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_MODIFY_EP0:
+		mausb_modifyep0_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS:
+		mausb_setusbdevaddress_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_UPDATE_DEV:
+		mausb_updatedev_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_USB_DEV_RESET:
+		mausb_usbdevreset_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_CANCEL_TRANSFER:
+		mausb_canceltransfer_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_PORT_CHANGED:
+		mausb_port_has_changed_event(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_PING:
+		mausb_send_mgmt_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_MGMT_MSG:
+		mausb_send_mgmt_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_DATA_MSG:
+		mausb_send_data_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG:
+		mausb_receive_data_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_URB_COMPLETE:
+		mausb_complete_urb(event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_ACK:
+		mausb_send_transfer_ack(dev, event);
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_ITERATOR_RESET:
+		mausb_iterator_reset(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_ITERATOR_SEEK:
+		mausb_iterator_seek(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_DELETE_MA_DEV:
+		mausb_delete_ma_dev(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_USER_FINISHED:
+		mausb_process_user_finished(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES:
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_NONE:
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT:
+		mausb_complete_timeout_event(dev, event);
+		break;
+	default:
+		break;
+	}
+
+	mausb_notify_completed_user_events(dev->ring_buffer);
+}
+
+static void mausb_hpal_kernel_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						work);
+	struct mausb_event *event;
+	int status;
+	u16 i;
+	u16 events;
+	u16 completed_events;
+	unsigned long flags;
+	struct mausb_ring_buffer *dev_mausb_ring = dev->ring_buffer;
+
+	spin_lock_irqsave(&dev->num_of_user_events_lock, flags);
+	events = dev->num_of_user_events;
+	completed_events = dev->num_of_completed_events;
+	dev->num_of_user_events = 0;
+	dev->num_of_completed_events = 0;
+	spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags);
+
+	status = mausb_ring_buffer_move_tail(dev_mausb_ring, completed_events);
+	if (status < 0) {
+		mausb_pr_err("Dequeue failed, status=%d", status);
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return;
+	}
+
+	for (i = 0; i < events; ++i) {
+		event = mausb_ring_current_from_user(dev_mausb_ring);
+		mausb_ring_next_from_user(dev_mausb_ring);
+		mausb_process_event(dev, event);
+	}
+}
+
 static void mausb_socket_disconnect_event(struct work_struct *work)
 {
 	struct mausb_device *dev = container_of(work, struct mausb_device,
 						socket_disconnect_work);
 	struct mausb_event event;
+	int status;
 
 	mausb_pr_info("madev_addr=%d", dev->madev_addr);
 
@@ -363,6 +560,11 @@ static void mausb_socket_disconnect_event(struct work_struct *work)
 		event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
 		event.data.device_id = dev->id;
 
+		status = mausb_enqueue_event_to_user(dev, &event);
+
+		mausb_pr_info("Sending notification to user that network is disconnected status=%d",
+			      status);
+
 		mausb_pr_info("Releasing MAUSB device ref");
 		kref_put(&dev->refcount, mausb_release_ma_dev_async);
 	}
@@ -427,6 +629,13 @@ static void mausb_delete_madev(struct work_struct *work)
 
 		mausb_insert_event(dev, &mausb_completion);
 
+		status = mausb_enqueue_event_to_user(dev, &event);
+		if (status < 0) {
+			mausb_remove_event(dev, &mausb_completion);
+			mausb_pr_err("Ring buffer full, enqueue failed");
+			return;
+		}
+
 		mausb_pr_debug("Deleting MAUSB device...");
 
 		status = wait_for_completion_interruptible_timeout(&completion,
@@ -449,10 +658,14 @@ static void mausb_delete_madev(struct work_struct *work)
 
 	mausb_clear_hcd_madev(dev->port_number);
 
+	mausb_ring_buffer_cleanup(dev->ring_buffer);
+	mausb_ring_buffer_destroy(dev->ring_buffer);
+
 	mausb_remove_madev_from_list(dev->madev_addr);
 
 	put_net(dev->net_ns);
 
+	kfree(dev->ring_buffer);
 	kfree(dev);
 	mausb_signal_empty_mss();
 
@@ -463,6 +676,7 @@ static void mausb_ping_work(struct work_struct *work)
 {
 	struct mausb_device *dev = container_of(work, struct mausb_device,
 						ping_work);
+	int status = 0;
 
 	if (mausb_start_connection_timer(dev) < 0) {
 		mausb_pr_err("Device disconnecting due to session timeout madev_addr=%d",
@@ -471,6 +685,13 @@ static void mausb_ping_work(struct work_struct *work)
 		queue_work(dev->workq, &dev->hcd_disconnect_work);
 		return;
 	}
+
+	status = mausb_ping_event_to_user(dev);
+
+	if (status < 0) {
+		mausb_pr_err("Ring buffer full");
+		return;
+	}
 }
 
 static void mausb_heartbeat_work(struct work_struct *work)
@@ -576,6 +797,7 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
 
 	dev->workq = workq;
 
+	INIT_WORK(&dev->work, mausb_hpal_kernel_work);
 	INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event);
 	INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event);
 	INIT_WORK(&dev->madev_delete_work, mausb_delete_madev);
@@ -601,6 +823,15 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
 	dev->madev_addr = madev_address;
 	dev->net_ns = get_net(current->nsproxy->net_ns);
 
+	if (!list_empty(&mss.available_ring_buffers)) {
+		dev->ring_buffer = container_of(mss.available_ring_buffers.next,
+						struct mausb_ring_buffer,
+						list_entry);
+		list_del(mss.available_ring_buffers.next);
+	} else {
+		mausb_pr_alert("Ring buffer for mausb device is not availbale!");
+	}
+
 	list_add_tail(&dev->list_entry, &mss.madev_list);
 
 	reinit_completion(&mss.empty);
@@ -659,6 +890,14 @@ int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
 	return 0;
 }
 
+void mausb_on_madev_connected(struct mausb_device *dev)
+{
+	struct mausb_event mausb_event;
+
+	mausb_dev_reset_req_event(&mausb_event);
+	mausb_enqueue_event_to_user(dev, &mausb_event);
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 				  u16 num_of_completed)
 {
@@ -683,9 +922,29 @@ int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 	return 0;
 }
 
+int mausb_enqueue_event_to_user(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	int status;
+
+	event->madev_addr = dev->madev_addr;
+	status = mausb_ring_buffer_put(dev->ring_buffer, event);
+	if (status < 0) {
+		mausb_pr_err("Ring buffer operation failed");
+		mausb_cleanup_ring_buffer_event(event);
+		return status;
+	}
+
+	mausb_notify_ring_events(dev->ring_buffer);
+	mausb_pr_debug("User-space notification sent.");
+
+	return 0;
+}
+
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request)
 {
+	int status;
 	struct mausb_event mausb_event;
 
 	mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
@@ -728,7 +987,12 @@ int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 		       &request->dev->route, sizeof(request->dev->route));
 	}
 
-	return 0;
+	status = mausb_enqueue_event_to_user(dev, &mausb_event);
+	if (status < 0)
+		mausb_pr_err("Failed to enqueue event to user-space ep_handle=%#x, status=%d",
+			     mausb_event.data.ep_handle, status);
+
+	return status;
 }
 
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status)
@@ -841,6 +1105,17 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
 	mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x",
 		       urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
 
+	if (!usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+		status = mausb_canceltransfer_event_to_user(ep_ctx->ma_dev,
+							    ep_ctx->dev_handle,
+							    ep_ctx->ep_handle,
+							    (uintptr_t)urb);
+		if (status < 0) {
+			mausb_pr_err("Failed to enqueue cancel transfer to user");
+			goto complete_urb;
+		}
+	}
+
 	memset(&mausb_event, 0, sizeof(mausb_event));
 
 	mausb_event.type   = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER;
@@ -855,6 +1130,13 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
 						MAUSB_DATA_MSG_DIRECTION_IN :
 						MAUSB_DATA_MSG_DIRECTION_OUT);
 
+	status = mausb_enqueue_event_to_user(ep_ctx->ma_dev, &mausb_event);
+	if (status < 0) {
+		mausb_pr_alert("Failed to enqueue event to user-space ep_handle=%#x, status=%d",
+			       mausb_event.data.ep_handle, status);
+		goto complete_urb;
+	}
+
 	if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) {
 		mausb_pr_alert("Failed to insert in tree urb=%p ep_handle=%#x, status=%d",
 			       urb, mausb_event.data.ep_handle, status);
@@ -916,6 +1198,7 @@ void mausb_deinitialize_mss(void)
 
 	wait_for_completion(&mss.empty);
 	mausb_pr_debug("Waiting for completion on disconnect_event ended");
+	mausb_stop_ring_events();
 
 	timeout = wait_for_completion_timeout(&mss.client_stopped, timeout);
 	mausb_pr_info("Remaining time after waiting for stopping client %ld",
@@ -1104,7 +1387,6 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
 int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 {
 	struct mausb_urb_ctx *urb_ctx;
-	int status = 0;
 
 	if (event->status != 0) {
 		mausb_pr_err("Event %d failed with status %d",
@@ -1119,10 +1401,9 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled for event=%d",
 			      event->type);
-		return status;
 	}
 
-	return status;
+	return 0;
 }
 
 int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
@@ -1142,8 +1423,10 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 
 	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
 
-	if (!urb_ctx)
+	if (!urb_ctx) {
+		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled");
+	}
 
 cleanup:
 	mausb_release_event_resources(event);
@@ -1279,6 +1562,7 @@ static void mausb_connect_callback(struct mausb_device *dev,
 	if (channel == MAUSB_ISOCH_CHANNEL) {
 		dev->channel_map[MAUSB_INTR_CHANNEL] =
 				dev->channel_map[MAUSB_CTRL_CHANNEL];
+		mausb_on_madev_connected(dev);
 	}
 }
 
@@ -1305,6 +1589,16 @@ static void mausb_handle_receive_event(struct mausb_device *dev,
 	}
 
 	mausb_reset_connection_timer(dev);
+
+	status = mausb_msg_received_event(&event,
+					  (struct ma_usb_hdr_common *)data,
+					  channel);
+
+	if (status == 0)
+		status = mausb_enqueue_event_to_user(dev, &event);
+
+	if (status < 0)
+		mausb_pr_err("Failed to enqueue, status=%d", status);
 }
 
 void mausb_ip_callback(void *ctx, enum mausb_channel channel,
@@ -1614,3 +1908,148 @@ u32 mausb_data_iterator_length(struct mausb_data_iter *iterator)
 {
 	return iterator->length;
 }
+
+static int mausb_ring_buffer_get(struct mausb_ring_buffer *ring,
+				 struct mausb_event *event)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 1) {
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	memcpy(event, ring->to_user_buffer + ring->tail, sizeof(*event));
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	ring->tail = (ring->tail + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+int mausb_ring_buffer_init(struct mausb_ring_buffer *ring)
+{
+	unsigned int page_order =
+		mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+				     sizeof(struct mausb_event));
+	ring->to_user_buffer =
+		(struct mausb_event *)__get_free_pages(GFP_KERNEL, page_order);
+	if (!ring->to_user_buffer)
+		return -ENOMEM;
+	ring->from_user_buffer = ring->to_user_buffer + MAUSB_RING_BUFFER_SIZE;
+	ring->head = 0;
+	ring->tail = 0;
+	ring->current_from_user = 0;
+	ring->buffer_full = false;
+	spin_lock_init(&ring->lock);
+
+	return 0;
+}
+
+int mausb_ring_buffer_put(struct mausb_ring_buffer *ring,
+			  struct mausb_event *event)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+
+	if (ring->buffer_full) {
+		mausb_pr_err("Ring buffer is full");
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+
+	if (CIRC_SPACE(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 2) {
+		mausb_pr_err("Ring buffer capacity exceeded, disconnecting device");
+		ring->buffer_full = true;
+		mausb_disconect_event_unsafe(ring, event->madev_addr);
+		ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	memcpy(ring->to_user_buffer + ring->head, event, sizeof(*event));
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+	mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < count) {
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	mausb_pr_debug("old HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	ring->tail = (ring->tail + (int)count) & (MAUSB_RING_BUFFER_SIZE - 1);
+	mausb_pr_debug("new HEAD=%d, TAIL=%d", ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring)
+{
+	struct mausb_event event;
+
+	while (mausb_ring_buffer_get(ring, &event) == 0)
+		mausb_cleanup_ring_buffer_event(&event);
+}
+
+void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring)
+{
+	unsigned int page_order =
+		mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+				     sizeof(struct mausb_event));
+	if (ring && ring->to_user_buffer)
+		free_pages((unsigned long)ring->to_user_buffer, page_order);
+}
+
+void mausb_cleanup_ring_buffer_event(struct mausb_event *event)
+{
+	mausb_pr_debug("event=%d", event->type);
+	switch (event->type) {
+	case MAUSB_EVENT_TYPE_SEND_DATA_MSG:
+		mausb_cleanup_send_data_msg_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG:
+		mausb_cleanup_received_data_msg_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER:
+		mausb_cleanup_delete_data_transfer_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_NONE:
+		break;
+	default:
+		mausb_pr_warn("Unknown event type");
+		break;
+	}
+}
+
+void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring,
+				  uint8_t madev_addr)
+{
+	struct mausb_event disconnect_event;
+	struct mausb_device *dev = mausb_get_dev_from_addr_unsafe(madev_addr);
+
+	if (!dev) {
+		mausb_pr_err("Device not found, madev_addr=%#x", madev_addr);
+		return;
+	}
+
+	disconnect_event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
+	disconnect_event.status = -EINVAL;
+	disconnect_event.madev_addr = madev_addr;
+
+	memcpy(ring->to_user_buffer + ring->head, &disconnect_event,
+	       sizeof(disconnect_event));
+
+	mausb_pr_info("Disconnect event added to ring buffer for madev_addr=%#x",
+		      madev_addr);
+	mausb_pr_info("Adding hcd_disconnect_work to dev workq, madev_addr=%#x",
+		      madev_addr);
+	queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
diff --git a/drivers/usb/mausb_host/hpal.h b/drivers/usb/mausb_host/hpal.h
index a04ed120ba5e..24846d4bc4a3 100644
--- a/drivers/usb/mausb_host/hpal.h
+++ b/drivers/usb/mausb_host/hpal.h
@@ -67,6 +67,7 @@ struct mss {
 struct mausb_device {
 	struct mausb_device_address dev_addr;
 	struct net		    *net_ns;
+	struct mausb_ring_buffer    *ring_buffer;
 	struct list_head	    list_entry;
 
 	struct mausb_ip_ctx *mgmt_channel;
@@ -133,6 +134,10 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
 
 int mausb_initiate_dev_connection(struct mausb_device_address device_address,
 				  u8 madev_address);
+int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
+				  u16 num_of_completed);
+int mausb_enqueue_event_to_user(struct mausb_device *dev,
+				struct mausb_event *event);
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request);
 int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
@@ -282,6 +287,33 @@ void mausb_reset_data_iterator(struct mausb_data_iter *iterator);
 void mausb_uninit_data_iterator(struct mausb_data_iter *iterator);
 void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta);
 
+struct mausb_ring_buffer {
+	atomic_t mausb_ring_events;
+	atomic_t mausb_completed_user_events;
+
+	struct mausb_event *to_user_buffer;
+	int		head;
+	int		tail;
+	spinlock_t	lock; /* Protect ring buffer */
+	u64		id;
+
+	struct mausb_event *from_user_buffer;
+	int current_from_user;
+
+	struct list_head list_entry;
+	bool buffer_full;
+};
+
+int mausb_ring_buffer_init(struct mausb_ring_buffer *ring);
+int mausb_ring_buffer_put(struct mausb_ring_buffer *ring,
+			  struct mausb_event *event);
+int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count);
+void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring);
+void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring);
+void mausb_cleanup_ring_buffer_event(struct mausb_event *event);
+void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring,
+				  uint8_t madev_addr);
+
 static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
 						unsigned int elem_size)
 {
@@ -292,4 +324,16 @@ static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
 	return order;
 }
 
+static inline
+struct mausb_event *mausb_ring_current_from_user(struct mausb_ring_buffer *ring)
+{
+	return ring->from_user_buffer + ring->current_from_user;
+}
+
+static inline void mausb_ring_next_from_user(struct mausb_ring_buffer *ring)
+{
+	ring->current_from_user = (ring->current_from_user + 1) &
+				  (MAUSB_RING_BUFFER_SIZE - 1);
+}
+
 #endif /* __MAUSB_HPAL_H__ */
diff --git a/drivers/usb/mausb_host/hpal_events.c b/drivers/usb/mausb_host/hpal_events.c
new file mode 100644
index 000000000000..ee2cfa253e83
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_events.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_events.h"
+
+#include <linux/slab.h>
+
+#include "hcd.h"
+#include "utils.h"
+
+void mausb_dev_reset_req_event(struct mausb_event *event)
+{
+	event->type = MAUSB_EVENT_TYPE_DEV_RESET;
+}
+
+static int mausb_mgmt_msg_received_event(struct mausb_event *event,
+					 struct ma_usb_hdr_common *hdr,
+					 enum mausb_channel channel)
+{
+	int status = 0;
+
+	mausb_pr_info("channel=%d, type=%d", channel, hdr->type);
+	if (hdr->length <= MAUSB_MAX_MGMT_SIZE) {
+		event->type = MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG;
+		memcpy(event->mgmt.mgmt_hdr.hdr, hdr, hdr->length);
+	} else {
+		mausb_pr_err("MGMT message to long, failed to copy");
+		status = -EINVAL;
+	}
+
+	kfree(hdr);
+	return status;
+}
+
+static int mausb_data_msg_received_event(struct mausb_event *event,
+					 struct ma_usb_hdr_common *hdr,
+					 enum mausb_channel channel)
+{
+	event->type		  = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
+	event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
+	event->data.device_id	  = (u16)((hdr->ssid << 8) | hdr->dev_addr);
+	event->data.ep_handle	  = hdr->handle.epv;
+	event->data.recv_buf	  = (uintptr_t)hdr;
+
+	memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
+
+	if (mausb_ctrl_transfer(hdr) &&
+	    hdr->length <= 2 * MAUSB_TRANSFER_HDR_SIZE) {
+		memcpy(event->data.hdr_ack,
+		       shift_ptr(hdr, MAUSB_TRANSFER_HDR_SIZE),
+		       (size_t)(hdr->length - MAUSB_TRANSFER_HDR_SIZE));
+	}
+
+	return 0;
+}
+
+static int mausb_isoch_msg_received_event(struct mausb_event *event,
+					  struct ma_usb_hdr_common *hdr,
+					  enum mausb_channel channel)
+{
+	event->type		  = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
+	event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
+	event->data.device_id	  = (u16)((hdr->ssid << 8) | hdr->dev_addr);
+	event->data.ep_handle	  = hdr->handle.epv;
+	event->data.recv_buf	  = (uintptr_t)hdr;
+
+	memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
+
+	return 0;
+}
+
+int mausb_msg_received_event(struct mausb_event *event,
+			     struct ma_usb_hdr_common *hdr,
+			     enum mausb_channel channel)
+{
+	mausb_pr_debug("channel=%d, type=%d", channel, hdr->type);
+	if (mausb_is_management_hdr_type(hdr->type))
+		return mausb_mgmt_msg_received_event(event, hdr, channel);
+	else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(TRANSFER))
+		return mausb_data_msg_received_event(event, hdr, channel);
+	else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(ISOCHTRANSFER))
+		return mausb_isoch_msg_received_event(event, hdr, channel);
+
+	mausb_pr_warn("Unknown event type event=%d", hdr->type);
+	kfree(hdr);
+	return -EBADR;
+}
+
+static void mausb_prepare_completion(struct mausb_completion *mausb_completion,
+				     struct completion *completion,
+				     struct mausb_event *event, u64 event_id)
+{
+	init_completion(completion);
+
+	mausb_completion->completion_event = completion;
+	mausb_completion->event_id	   = event_id;
+	mausb_completion->mausb_event	   = event;
+}
+
+static int mausb_wait_for_completion(struct mausb_event *event, u64 event_id,
+				     struct mausb_device *dev)
+{
+	struct completion	completion;
+	struct mausb_completion mausb_completion;
+	long status;
+	unsigned long timeout;
+
+	mausb_prepare_completion(&mausb_completion, &completion, event,
+				 event_id);
+	mausb_insert_event(dev, &mausb_completion);
+
+	status = mausb_enqueue_event_to_user(dev, event);
+	if (status < 0) {
+		mausb_remove_event(dev, &mausb_completion);
+		mausb_pr_err("Ring buffer full, event_id=%lld", event_id);
+		return (int)status;
+	}
+
+	timeout = msecs_to_jiffies(MANAGEMENT_EVENT_TIMEOUT);
+	status = wait_for_completion_interruptible_timeout(&completion,
+							   timeout);
+
+	mausb_remove_event(dev, &mausb_completion);
+
+	if (status == 0) {
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
+				     u8 device_speed,
+				     u32 route_string,
+				     u16 hub_dev_handle,
+				     u16 parent_hs_hub_dev_handle,
+				     u16 parent_hs_hub_port, u16 mtt,
+				     u8 lse, s32 *usb_dev_handle)
+{
+	struct mausb_event event;
+	int status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_USB_DEV_HANDLE;
+	event.mgmt.dev_handle.device_speed	 = device_speed;
+	event.mgmt.dev_handle.route_string	 = route_string;
+	event.mgmt.dev_handle.hub_dev_handle	 = hub_dev_handle;
+	event.mgmt.dev_handle.parent_hs_hub_port = parent_hs_hub_port;
+	event.mgmt.dev_handle.mtt		 = mtt;
+	event.mgmt.dev_handle.lse		 = lse;
+	event.mgmt.dev_handle.event_id		 = event_id;
+	event.madev_addr			 = dev->madev_addr;
+	event.mgmt.dev_handle.parent_hs_hub_dev_handle =
+						   parent_hs_hub_dev_handle;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Usbdevhandle failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*usb_dev_handle = event.mgmt.dev_handle.dev_handle;
+
+	return 0;
+}
+
+int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.dev_handle.event_id);
+}
+
+int mausb_ephandle_event_to_user(struct mausb_device *dev,
+				 u16 device_handle,
+				 u16 descriptor_size, void *descriptor,
+				 u16 *ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			     = MAUSB_EVENT_TYPE_EP_HANDLE;
+	event.mgmt.ep_handle.device_handle   = device_handle;
+	event.mgmt.ep_handle.descriptor_size = descriptor_size;
+	event.mgmt.ep_handle.event_id	     = event_id;
+	event.madev_addr		     = dev->madev_addr;
+
+	memcpy(event.mgmt.ep_handle.descriptor, descriptor, descriptor_size);
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Ephandle failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*ep_handle = event.mgmt.ep_handle.ep_handle;
+
+	return 0;
+}
+
+int mausb_ephandle_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_handle.event_id);
+}
+
+int mausb_epactivate_event_to_user(struct mausb_device *dev,
+				   u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE;
+	event.mgmt.ep_activate.device_handle = device_handle;
+	event.mgmt.ep_activate.ep_handle     = ep_handle;
+	event.mgmt.ep_activate.event_id	     = event_id;
+	event.madev_addr		     = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epactivate failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epactivate_event_from_user(struct mausb_device *dev,
+				     struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+		event->mgmt.ep_activate.event_id);
+}
+
+int mausb_epinactivate_event_to_user(struct mausb_device *dev,
+				     u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE;
+	event.mgmt.ep_inactivate.device_handle	= device_handle;
+	event.mgmt.ep_inactivate.ep_handle	= ep_handle;
+	event.mgmt.ep_inactivate.event_id	= event_id;
+	event.madev_addr			= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epinactivate failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epinactivate_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+				  event->mgmt.ep_inactivate.event_id);
+}
+
+int mausb_epreset_event_to_user(struct mausb_device *dev,
+				u16 device_handle, u16 ep_handle,
+				u8 tsp_flag)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			  = MAUSB_EVENT_TYPE_EP_HANDLE_RESET;
+	event.mgmt.ep_reset.device_handle = device_handle;
+	event.mgmt.ep_reset.ep_handle	  = ep_handle;
+	event.mgmt.ep_reset.tsp		  = tsp_flag;
+	event.mgmt.ep_reset.event_id	  = event_id;
+	event.madev_addr		  = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epreset failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epreset_event_from_user(struct mausb_device *dev,
+				  struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_reset.event_id);
+}
+
+int mausb_epdelete_event_to_user(struct mausb_device *dev,
+				 u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			   = MAUSB_EVENT_TYPE_EP_HANDLE_DELETE;
+	event.mgmt.ep_delete.device_handle = device_handle;
+	event.mgmt.ep_delete.ep_handle	   = ep_handle;
+	event.mgmt.ep_delete.event_id	   = event_id;
+	event.madev_addr		   = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Epdelete failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epdelete_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_delete.event_id);
+}
+
+int mausb_modifyep0_event_to_user(struct mausb_device *dev,
+				  u16 device_handle, u16 *ep_handle,
+				  __le16 max_packet_size)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type				= MAUSB_EVENT_TYPE_MODIFY_EP0;
+	event.mgmt.modify_ep0.device_handle	= device_handle;
+	event.mgmt.modify_ep0.ep_handle		= *ep_handle;
+	event.mgmt.modify_ep0.max_packet_size	= max_packet_size;
+	event.mgmt.modify_ep0.event_id		= event_id;
+	event.madev_addr			= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Modifyep0 failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*ep_handle = event.mgmt.modify_ep0.ep_handle;
+
+	return 0;
+}
+
+int mausb_modifyep0_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.modify_ep0.event_id);
+}
+
+int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
+					 u16 device_handle,
+					 u16 response_timeout)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS;
+	event.mgmt.set_usb_dev_address.device_handle	= device_handle;
+	event.mgmt.set_usb_dev_address.response_timeout	= response_timeout;
+	event.mgmt.set_usb_dev_address.event_id		= event_id;
+	event.madev_addr				= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Setusbdevaddress failed, event_id=%lld",
+			     event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
+					   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+		event->mgmt.set_usb_dev_address.event_id);
+}
+
+static void
+mausb_init_device_descriptor(struct ma_usb_updatedevreq_desc *update_descriptor,
+			     struct usb_device_descriptor *device_descriptor)
+{
+	update_descriptor->usb20.bLength = device_descriptor->bLength;
+	update_descriptor->usb20.bDescriptorType =
+					device_descriptor->bDescriptorType;
+	update_descriptor->usb20.bcdUSB = device_descriptor->bcdUSB;
+	update_descriptor->usb20.bDeviceClass =
+					device_descriptor->bDeviceClass;
+	update_descriptor->usb20.bDeviceSubClass =
+					device_descriptor->bDeviceSubClass;
+	update_descriptor->usb20.bDeviceProtocol =
+					device_descriptor->bDeviceProtocol;
+	update_descriptor->usb20.bMaxPacketSize0 =
+					device_descriptor->bMaxPacketSize0;
+	update_descriptor->usb20.idVendor  = device_descriptor->idVendor;
+	update_descriptor->usb20.idProduct = device_descriptor->idProduct;
+	update_descriptor->usb20.bcdDevice = device_descriptor->bcdDevice;
+	update_descriptor->usb20.iManufacturer =
+					device_descriptor->iManufacturer;
+	update_descriptor->usb20.iProduct  = device_descriptor->iProduct;
+	update_descriptor->usb20.iSerialNumber =
+					device_descriptor->iSerialNumber;
+	update_descriptor->usb20.bNumConfigurations =
+					device_descriptor->bNumConfigurations;
+}
+
+int mausb_updatedev_event_to_user(struct mausb_device *dev,
+				  u16 device_handle,
+				  u16 max_exit_latency, u8 hub,
+				  u8 number_of_ports, u8 mtt,
+				  u8 ttt, u8 integrated_hub_latency,
+				  struct usb_device_descriptor *dev_descriptor)
+{
+	struct mausb_event event;
+	int status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_UPDATE_DEV;
+	event.mgmt.update_dev.device_handle	     = device_handle;
+	event.mgmt.update_dev.max_exit_latency	     = max_exit_latency;
+	event.mgmt.update_dev.hub		     = hub;
+	event.mgmt.update_dev.number_of_ports	     = number_of_ports;
+	event.mgmt.update_dev.mtt		     = mtt;
+	event.mgmt.update_dev.ttt		     = ttt;
+	event.mgmt.update_dev.integrated_hub_latency = integrated_hub_latency;
+	event.mgmt.update_dev.event_id		     = event_id;
+	event.madev_addr			     = dev->madev_addr;
+
+	mausb_init_device_descriptor(&event.mgmt.update_dev.update_descriptor,
+				     dev_descriptor);
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Updatedev failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_updatedev_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.update_dev.event_id);
+}
+
+int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
+					 u16 dev_handle)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type = MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT;
+	event.mgmt.usb_dev_disconnect.device_handle = dev_handle;
+	event.madev_addr			    = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		mausb_pr_err("Ring buffer full, usbdevdisconnect failed");
+
+	return status;
+}
+
+int mausb_ping_event_to_user(struct mausb_device *dev)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type	 = MAUSB_EVENT_TYPE_PING;
+	event.madev_addr = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		mausb_pr_err("Ring buffer full, devdisconnect failed");
+
+	return status;
+}
+
+__attribute__((unused))
+static int mausb_devdisconnect_event_to_user(struct mausb_device *dev)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type	 = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
+	event.madev_addr = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		mausb_pr_err("Ring buffer full, devdisconnect failed");
+
+	return status;
+}
+
+int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
+				    u16 device_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			       = MAUSB_EVENT_TYPE_USB_DEV_RESET;
+	event.mgmt.usb_dev_reset.device_handle = device_handle;
+	event.mgmt.usb_dev_reset.event_id      = event_id;
+	event.madev_addr		       = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		mausb_pr_err("Usbdevreset failed, event_id=%lld", event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
+				      struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+				  event->mgmt.usb_dev_reset.event_id);
+}
+
+int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
+				       u16 device_handle, u16 ep_handle,
+				       uintptr_t urb)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type = MAUSB_EVENT_TYPE_CANCEL_TRANSFER;
+	event.mgmt.cancel_transfer.device_handle = device_handle;
+	event.mgmt.cancel_transfer.ep_handle	 = ep_handle;
+	event.mgmt.cancel_transfer.urb		 = urb;
+	event.madev_addr			 = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0) {
+		mausb_pr_err("Ring buffer full, canceltransfer failed");
+		return status;
+	}
+
+	return status;
+}
+
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event)
+{
+	mausb_pr_debug("");
+	return 0;
+}
+
+void mausb_cleanup_send_data_msg_event(struct mausb_event *event)
+{
+	mausb_complete_urb(event);
+}
+
+void mausb_cleanup_received_data_msg_event(struct mausb_event *event)
+{
+	mausb_release_event_resources(event);
+}
+
+void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
+
+	urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+	if (!urb_ctx) {
+		mausb_pr_warn("Urb=%p is not in tree", urb);
+		return;
+	}
+
+	/* Deallocate urb_ctx */
+	mausb_uninit_data_iterator(&urb_ctx->iterator);
+	kfree(urb_ctx);
+
+	urb->status = -EPROTO;
+	urb->actual_length = 0;
+	atomic_dec(&urb->use_count);
+	usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+}
diff --git a/drivers/usb/mausb_host/hpal_events.h b/drivers/usb/mausb_host/hpal_events.h
new file mode 100644
index 000000000000..8775f9c3438c
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_events.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_EVENTS_H__
+#define __MAUSB_HPAL_EVENTS_H__
+
+#include "hpal.h"
+#include "ip_link.h"
+#include "mausb_event.h"
+
+#define MANAGEMENT_EVENT_TIMEOUT 3000
+
+int mausb_msg_received_event(struct mausb_event *event,
+			     struct ma_usb_hdr_common *hdr,
+			     enum mausb_channel channel);
+int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
+				     u8 device_speed,
+				     u32 route_string,
+				     u16 hub_dev_handle,
+				     u16 parent_hs_hub_dev_handle,
+				     u16 parent_hs_hub_port, u16 mtt,
+				     u8 lse, s32 *usb_dev_handle);
+int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event);
+int mausb_ephandle_event_to_user(struct mausb_device *dev, u16 device_handle,
+				 u16 descriptor_size, void *descriptor,
+				 u16 *ep_handle);
+int mausb_ephandle_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event);
+int mausb_epactivate_event_to_user(struct mausb_device *dev,
+				   u16 device_handle, u16 ep_handle);
+int mausb_epactivate_event_from_user(struct mausb_device *dev,
+				     struct mausb_event *event);
+int mausb_epinactivate_event_to_user(struct mausb_device *dev,
+				     u16 device_handle,
+				     u16 ep_handle);
+int mausb_epinactivate_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event);
+int mausb_epreset_event_to_user(struct mausb_device *dev,
+				u16 device_handle, u16 ep_handle,
+				u8 tsp_flag);
+int mausb_epreset_event_from_user(struct mausb_device *dev,
+				  struct mausb_event *event);
+int mausb_epdelete_event_to_user(struct mausb_device *dev,
+				 u16 device_handle, u16 ep_handle);
+int mausb_epdelete_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event);
+int mausb_modifyep0_event_to_user(struct mausb_device *dev,
+				  u16 device_handle, u16 *ep_handle,
+				  __le16 max_packet_size);
+int mausb_modifyep0_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event);
+int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
+					 u16 device_handle,
+					 u16 response_timeout);
+int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
+					   struct mausb_event *event);
+int mausb_updatedev_event_to_user(struct mausb_device *dev,
+				  u16 device_handle,
+				  u16 max_exit_latency, u8 hub,
+				  u8 number_of_ports, u8 mtt,
+				  u8 ttt, u8 integrated_hub_latency,
+				  struct usb_device_descriptor *dev_descriptor);
+int mausb_updatedev_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event);
+int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
+					 u16 dev_handle);
+int mausb_ping_event_to_user(struct mausb_device *dev);
+int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
+				    u16 device_handle);
+int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
+				      struct mausb_event *event);
+int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
+				       u16 device_handle, u16 ep_handle,
+				       uintptr_t urb);
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event);
+
+void mausb_dev_reset_req_event(struct mausb_event *event);
+void mausb_cleanup_send_data_msg_event(struct mausb_event *event);
+void mausb_cleanup_received_data_msg_event(struct mausb_event *event);
+void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_EVENTS_H__ */
diff --git a/drivers/usb/mausb_host/mausb_driver_status.h b/drivers/usb/mausb_host/mausb_driver_status.h
new file mode 100644
index 000000000000..9b55befe9cae
--- /dev/null
+++ b/drivers/usb/mausb_host/mausb_driver_status.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_DRIVER_STATUS_H__
+#define __MAUSB_MAUSB_DRIVER_STATUS_H__
+
+#define MAUSB_DRIVER_BAD_READ_BUFFER_SIZE  -1
+#define MAUSB_DRIVER_RING_EVENTS_STOPPED   -2
+#define MAUSB_DRIVER_SYSTEM_SUSPENDED      -3
+#define MAUSB_DRIVER_COPY_TO_BUFFER_FAILED -4
+
+#define MAUSB_DRIVER_READ_TIMEOUT 0
+#define MAUSB_DRIVER_READ_ERROR  -1
+#define MAUSB_DRIVER_WRITE_ERROR  -2
+
+#endif /* __MAUSB_MAUSB_DRIVER_STATUS_H__ */
diff --git a/drivers/usb/mausb_host/utils.c b/drivers/usb/mausb_host/utils.c
index c055b578e402..3e0ee4a5d158 100644
--- a/drivers/usb/mausb_host/utils.c
+++ b/drivers/usb/mausb_host/utils.c
@@ -13,6 +13,8 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 
+#include "mausb_driver_status.h"
+
 #define MAUSB_KERNEL_DEV_NAME "mausb_host"
 #define MAUSB_READ_DEVICE_TIMEOUT_MS 500
 
@@ -20,6 +22,47 @@ static dev_t mausb_major_kernel;
 static struct cdev  mausb_kernel_dev;
 static struct class *mausb_kernel_class;
 
+static void mausb_vm_open(struct vm_area_struct *vma)
+{
+	mausb_pr_debug("");
+}
+
+static void mausb_vm_close(struct vm_area_struct *vma)
+{
+	struct mausb_ring_buffer *buffer = NULL, *next = NULL;
+	unsigned long flags = 0;
+	u64 ring_buffer_id = *(u64 *)(vma->vm_private_data);
+
+	mausb_pr_info("Releasing ring buffer with id: %llu", ring_buffer_id);
+	spin_lock_irqsave(&mss.lock, flags);
+	list_for_each_entry_safe(buffer, next, &mss.available_ring_buffers,
+				 list_entry) {
+		if (buffer->id == ring_buffer_id) {
+			list_del(&buffer->list_entry);
+			mausb_ring_buffer_destroy(buffer);
+			kfree(buffer);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+/*
+ * mausb_vm_fault is called the first time a memory area is accessed which is
+ * not in memory
+ */
+static vm_fault_t mausb_vm_fault(struct vm_fault *vmf)
+{
+	mausb_pr_debug("");
+	return 0;
+}
+
+static const struct vm_operations_struct mausb_vm_ops = {
+	.open  = mausb_vm_open,
+	.close = mausb_vm_close,
+	.fault = mausb_vm_fault,
+};
+
 static int mausb_file_open(struct inode *inode, struct file *filp)
 {
 	filp->private_data = NULL;
@@ -35,9 +78,213 @@ static int mausb_file_close(struct inode *inode, struct file *filp)
 	return 0;
 }
 
+static ssize_t mausb_file_read(struct file *filp, char __user *user_buffer,
+			       size_t size, loff_t *offset)
+{
+	ssize_t num_of_bytes_to_read = MAUSB_MAX_NUM_OF_MA_DEVS *
+				       sizeof(struct mausb_events_notification);
+	unsigned long num_of_bytes_not_copied;
+	int completed_events;
+	int ring_events;
+	struct mausb_ring_buffer *ring_buffer;
+	struct mausb_device	 *dev;
+	struct completion	 *ring_has_events;
+	u8 current_device = 0;
+	s8 fail_ret_val;
+	unsigned long flags;
+	unsigned long timeout;
+	long status;
+
+	/* Reset heartbeat timer events */
+	mausb_reset_heartbeat_cnt();
+
+	if ((ssize_t)size != num_of_bytes_to_read) {
+		mausb_pr_alert("The actual size differs from the expected number of bytes");
+		fail_ret_val = MAUSB_DRIVER_BAD_READ_BUFFER_SIZE;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	/* If suspend/hibernate happened delete all devices */
+	if (atomic_xchg(&mss.num_of_transitions_to_sleep, 0)) {
+		mausb_pr_alert("Suspend system event detected");
+		fail_ret_val = MAUSB_DRIVER_SYSTEM_SUSPENDED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	ring_has_events = &mss.rings_events.mausb_ring_has_events;
+	timeout = msecs_to_jiffies(MAUSB_READ_DEVICE_TIMEOUT_MS);
+	status = wait_for_completion_interruptible_timeout(ring_has_events,
+							   timeout);
+	reinit_completion(ring_has_events);
+
+	if (atomic_read(&mss.rings_events.mausb_stop_reading_ring_events)) {
+		mausb_pr_alert("Ring events stopped");
+		fail_ret_val = MAUSB_DRIVER_RING_EVENTS_STOPPED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	/* There are no new events - waiting for events hit timeout */
+	if (status == 0)
+		return MAUSB_DRIVER_READ_TIMEOUT;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mss.events[current_device].madev_addr = dev->madev_addr;
+		ring_buffer = dev->ring_buffer;
+		ring_events = atomic_xchg(&ring_buffer->mausb_ring_events, 0);
+		completed_events =
+			atomic_xchg(&ring_buffer->mausb_completed_user_events,
+				    0);
+		mss.events[current_device].num_of_events = (u16)ring_events;
+		mss.events[current_device].num_of_completed_events =
+				(u16)completed_events;
+		if (++current_device == MAUSB_MAX_NUM_OF_MA_DEVS)
+			break;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	num_of_bytes_to_read =
+		(ssize_t)(current_device *
+			  sizeof(struct mausb_events_notification));
+	num_of_bytes_not_copied =
+		copy_to_user(user_buffer, &mss.events,
+			     (unsigned long)num_of_bytes_to_read);
+
+	mausb_pr_debug("num_of_bytes_not_copied %lu, num_of_bytes_to_read %zd",
+		       num_of_bytes_not_copied, num_of_bytes_to_read);
+
+	if (num_of_bytes_not_copied) {
+		fail_ret_val = MAUSB_DRIVER_COPY_TO_BUFFER_FAILED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			mausb_pr_warn("Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	return num_of_bytes_to_read;
+}
+
+static ssize_t mausb_file_write(struct file *filp, const char __user *buffer,
+				size_t size, loff_t *offset)
+{
+	ssize_t num_of_bytes_to_write =
+				sizeof(struct mausb_events_notification);
+	struct mausb_events_notification notification;
+	unsigned long flags;
+	struct mausb_device *dev;
+
+	if (size != (size_t)num_of_bytes_to_write) {
+		mausb_pr_alert("The actual size differs from the expected number of bytes");
+		return MAUSB_DRIVER_WRITE_ERROR;
+	}
+
+	if (copy_from_user(&notification, buffer, size))
+		return MAUSB_DRIVER_WRITE_ERROR;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(notification.madev_addr);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return 0;
+	}
+
+	spin_lock(&dev->num_of_user_events_lock);
+	dev->num_of_user_events += notification.num_of_events;
+	dev->num_of_completed_events += notification.num_of_completed_events;
+	spin_unlock(&dev->num_of_user_events_lock);
+
+	queue_work(dev->workq, &dev->work);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return num_of_bytes_to_write;
+}
+
+static inline unsigned long mausb_ring_buffer_length(void)
+{
+	int page_order = mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+					      sizeof(struct mausb_event));
+	return PAGE_SIZE << page_order;
+}
+
+static int mausb_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	unsigned long size = vma->vm_end - vma->vm_start;
+	int ret;
+	struct page *page = NULL;
+	unsigned long flags = 0;
+	struct mausb_ring_buffer *ring_buffer = kzalloc(sizeof(*ring_buffer),
+							GFP_KERNEL);
+	if (!ring_buffer)
+		return -ENOMEM;
+
+	ret = mausb_ring_buffer_init(ring_buffer);
+	if (ret < 0) {
+		mausb_pr_err("Ring buffer init failed");
+		goto release_ring_buffer;
+	}
+
+	vma->vm_private_data = kzalloc(sizeof(ring_buffer->id), GFP_KERNEL);
+	if (!vma->vm_private_data) {
+		ret = -ENOMEM;
+		goto release_ring_buffer;
+	}
+
+	filp->private_data = vma->vm_private_data;
+
+	if (size > mausb_ring_buffer_length()) {
+		mausb_pr_err("Invalid memory size to map");
+		ret = -EINVAL;
+		goto release_ring_buffer;
+	}
+
+	vma->vm_ops = &mausb_vm_ops;
+	mausb_vm_open(vma);
+
+	page = virt_to_page(ring_buffer->to_user_buffer);
+	ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size,
+			      vma->vm_page_prot);
+	if (ret < 0) {
+		mausb_pr_err("Could not map the address area");
+		goto release_ring_buffer;
+	}
+
+	spin_lock_irqsave(&mss.lock, flags);
+	ring_buffer->id = mss.ring_buffer_id++;
+	*(u64 *)(vma->vm_private_data) = ring_buffer->id;
+	list_add_tail(&ring_buffer->list_entry, &mss.available_ring_buffers);
+	mausb_pr_info("Allocated ring buffer with id: %llu", ring_buffer->id);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+
+release_ring_buffer:
+	mausb_ring_buffer_destroy(ring_buffer);
+	kfree(ring_buffer);
+	return ret;
+}
+
 static const struct file_operations mausb_file_ops = {
 	.open	 = mausb_file_open,
 	.release = mausb_file_close,
+	.read	 = mausb_file_read,
+	.write   = mausb_file_write,
+	.mmap	 = mausb_mmap,
 };
 
 int mausb_create_dev(void)
@@ -83,3 +330,29 @@ void mausb_cleanup_dev(int device_created)
 
 	unregister_chrdev_region(mausb_major_kernel, 1);
 }
+
+void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer)
+{
+	int completed;
+
+	completed =
+		atomic_inc_return(&ring_buffer->mausb_completed_user_events);
+	mausb_pr_debug("mausb_completed_user_events INCREMENTED %d", completed);
+	if (completed > MAUSB_RING_BUFFER_SIZE / 16)
+		complete(&mss.rings_events.mausb_ring_has_events);
+}
+
+void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer)
+{
+	int events;
+
+	events = atomic_inc_return(&ring_buffer->mausb_ring_events);
+	if (events == 1)
+		complete(&mss.rings_events.mausb_ring_has_events);
+}
+
+void mausb_stop_ring_events(void)
+{
+	atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 1);
+	complete(&mss.rings_events.mausb_ring_has_events);
+}
diff --git a/drivers/usb/mausb_host/utils.h b/drivers/usb/mausb_host/utils.h
index 9adf4122e64d..54acd8585ad3 100644
--- a/drivers/usb/mausb_host/utils.h
+++ b/drivers/usb/mausb_host/utils.h
@@ -5,6 +5,8 @@
 #ifndef __MAUSB_UTILS_H__
 #define __MAUSB_UTILS_H__
 
+#include "hpal.h"
+
 #if defined(MAUSB_NO_LOGS)
 #define mausb_pr_logs(...)
 #else
@@ -36,5 +38,8 @@
 
 int mausb_create_dev(void);
 void mausb_cleanup_dev(int device_created);
+void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer);
+void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer);
+void mausb_stop_ring_events(void);
 
 #endif /* __MAUSB_UTILS_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v5 8/8] usb: mausb_host: Process MA-USB data packets
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (6 preceding siblings ...)
  2020-04-25  9:19     ` [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
@ 2020-04-25  9:19     ` vladimir.stankovic
  2020-04-28 11:04     ` [PATCH v5 0/8] Add MA USB Host driver Greg KH
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
  9 siblings, 0 replies; 62+ messages in thread
From: vladimir.stankovic @ 2020-04-25  9:19 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Added implementation of MA-USB data&isoch packets processing logic,
both for IN and OUT directions.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/mausb_host/Makefile    |   1 +
 drivers/usb/mausb_host/hpal.c      |  31 +-
 drivers/usb/mausb_host/hpal_data.c | 714 +++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal_data.h |  34 ++
 4 files changed, 778 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal_data.c
 create mode 100644 drivers/usb/mausb_host/hpal_data.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index fd2a36a04ad6..a5fd033c002e 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -12,5 +12,6 @@ mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
 mausb_host-y += hpal_events.o
+mausb_host-y += hpal_data.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index bf678f2707b9..cc46f3c5e2e6 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -11,6 +11,7 @@
 #include <linux/uio.h>
 
 #include "hcd.h"
+#include "hpal_data.h"
 #include "hpal_events.h"
 #include "utils.h"
 
@@ -1387,6 +1388,7 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
 int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 {
 	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
 
 	if (event->status != 0) {
 		mausb_pr_err("Event %d failed with status %d",
@@ -1401,9 +1403,22 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled for event=%d",
 			      event->type);
+		return status;
 	}
 
-	return 0;
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_send_isoch_in_msg(dev, event);
+		else
+			status = mausb_send_isoch_out_msg(dev, event, urb_ctx);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_send_in_data_msg(dev, event);
+		else
+			status = mausb_send_out_data_msg(dev, event, urb_ctx);
+	}
+
+	return status;
 }
 
 int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
@@ -1426,6 +1441,19 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 	if (!urb_ctx) {
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled");
+		goto cleanup;
+	}
+
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			mausb_receive_isoch_in_data(dev, event, urb_ctx);
+		else
+			mausb_receive_isoch_out(event);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			mausb_receive_in_data(event, urb_ctx);
+		else
+			mausb_receive_out_data(event, urb_ctx);
 	}
 
 cleanup:
@@ -1593,7 +1621,6 @@ static void mausb_handle_receive_event(struct mausb_device *dev,
 	status = mausb_msg_received_event(&event,
 					  (struct ma_usb_hdr_common *)data,
 					  channel);
-
 	if (status == 0)
 		status = mausb_enqueue_event_to_user(dev, &event);
 
diff --git a/drivers/usb/mausb_host/hpal_data.c b/drivers/usb/mausb_host/hpal_data.c
new file mode 100644
index 000000000000..4357a23afea0
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_data.c
@@ -0,0 +1,714 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_data.h"
+
+#include <linux/slab.h>
+#include <linux/uio.h>
+
+#include "hcd.h"
+#include "hpal.h"
+#include "hpal_events.h"
+#include "utils.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[2];
+	struct urb *urb   = (struct urb *)(event->data.urb);
+	bool setup_packet = (usb_endpoint_xfer_control(&urb->ep->desc) &&
+			     urb->setup_packet);
+	u32 kvec_num = setup_packet ? 2 : 1;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= kvec_num;
+	data_to_send.length	= MAUSB_TRANSFER_HDR_SIZE +
+			(setup_packet ? MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base = event->data.hdr;
+	kvec[0].iov_len  = MAUSB_TRANSFER_HDR_SIZE;
+
+	/* Prepare setup packet kvec */
+	if (setup_packet) {
+		kvec[1].iov_base = urb->setup_packet;
+		kvec[1].iov_len  = MAUSB_CONTROL_SETUP_SIZE;
+	}
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+	struct mausb_data_iter *iterator     = &urb_ctx->iterator;
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	void *buffer;
+	u32 payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE;
+	u32 data_written = 0;
+
+	buffer = shift_ptr(common_hdr, MAUSB_TRANSFER_HDR_SIZE);
+	data_written = mausb_data_iterator_write(iterator, buffer,
+						 payload_size);
+
+	mausb_pr_debug("data_written=%d, payload_size=%d", data_written,
+		       payload_size);
+	event->data.rem_transfer_size -= data_written;
+
+	if (event->data.transfer_eot) {
+		mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+			       event->data.transfer_size,
+			       event->data.rem_transfer_size, event->status);
+		mausb_complete_request(urb, event->data.transfer_size -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static int
+mausb_init_data_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				 struct list_head *chunks_list,
+				 u32 *num_of_data_chunks)
+{
+	int status = mausb_add_data_chunk(common_hdr, MAUSB_TRANSFER_HDR_SIZE,
+					  chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int mausb_init_control_data_chunk(struct mausb_event *event,
+					 struct list_head *chunks_list,
+					 u32 *num_of_data_chunks)
+{
+	int status;
+	void *buffer = ((struct urb *)event->data.urb)->setup_packet;
+
+	if (!event->data.first_control_packet)
+		return 0;
+
+	status = mausb_add_data_chunk(buffer, MAUSB_CONTROL_SETUP_SIZE,
+				      chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int
+mausb_prepare_transfer_packet(struct mausb_kvec_data_wrapper *wrapper,
+			      struct mausb_event *event,
+			      struct mausb_data_iter *iterator)
+{
+	u32 num_of_data_chunks		= 0;
+	u32 num_of_payload_data_chunks	= 0;
+	u32 payload_data_size		= 0;
+	int status = 0;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	struct ma_usb_hdr_common *data_hdr = (struct ma_usb_hdr_common *)
+			event->data.hdr;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_data_out_header_chunk(data_hdr, &chunks_list,
+					     &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/*
+	 * Initialize data chunk for MAUSB control setup packet and
+	 * add it to chunks list
+	 */
+	if (mausb_init_control_data_chunk(event, &chunks_list,
+					  &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size =
+			((struct ma_usb_hdr_common *)event->data.hdr)->length -
+			 MAUSB_TRANSFER_HDR_SIZE -
+			 (event->data.first_control_packet ?
+			  MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	if (mausb_data_iterator_read(iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks: /* Cleanup all allocated data chunks */
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	int status;
+	struct mausb_kvec_data_wrapper data;
+	enum mausb_channel channel;
+
+	status = mausb_prepare_transfer_packet(&data, event,
+					       &urb_ctx->iterator);
+
+	if (status < 0) {
+		mausb_pr_err("Failed to prepare transfer packet");
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data);
+
+	kfree(data.kvec);
+
+	return status;
+}
+
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size, event->data.rem_transfer_size,
+		       event->status);
+
+	if (event->data.transfer_eot) {
+		mausb_complete_request(urb, urb->transfer_buffer_length -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static inline u32
+__mausb_isoch_prepare_read_size_block(struct ma_usb_hdr_isochreadsizeblock_std *
+				      isoch_readsize_block, struct urb *urb)
+{
+	u32 i;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+
+	if (number_of_packets == 0)
+		return 0;
+
+	isoch_readsize_block->service_intervals  = number_of_packets;
+	isoch_readsize_block->max_segment_length =
+					(u32)urb->iso_frame_desc[0].length;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+
+	return sizeof(struct ma_usb_hdr_isochreadsizeblock_std);
+}
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	u32 read_size_block_length = 0;
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[MAUSB_ISOCH_IN_KVEC_NUM];
+	struct ma_usb_hdr_isochtransfer_optional opt_isoch_hdr;
+	struct ma_usb_hdr_isochreadsizeblock_std isoch_readsize_block;
+	struct ma_usb_hdr_common *hdr =
+				(struct ma_usb_hdr_common *)event->data.hdr;
+	struct urb *urb = (struct urb *)event->data.urb;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= 0;
+	data_to_send.length	= 0;
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base     = event->data.hdr;
+	kvec[0].iov_len	     = MAUSB_TRANSFER_HDR_SIZE;
+	data_to_send.length += (u32)kvec[0].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare optional header kvec */
+	opt_isoch_hdr.timestamp = MA_USB_TRANSFER_RESERVED;
+	opt_isoch_hdr.mtd	= MA_USB_TRANSFER_RESERVED;
+
+	kvec[1].iov_base     = &opt_isoch_hdr;
+	kvec[1].iov_len	     = sizeof(struct ma_usb_hdr_isochtransfer_optional);
+	data_to_send.length += (u32)kvec[1].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare read size blocks */
+	read_size_block_length =
+		__mausb_isoch_prepare_read_size_block(&isoch_readsize_block,
+						      urb);
+	if (read_size_block_length > 0) {
+		kvec[2].iov_base     = &isoch_readsize_block;
+		kvec[2].iov_len	     = read_size_block_length;
+		data_to_send.length += (u32)kvec[2].iov_len;
+		data_to_send.kvec_num++;
+	}
+
+	hdr->length = (u16)data_to_send.length;
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+static void __mausb_process_in_isoch_short_resp(struct mausb_event *event,
+						struct ma_usb_hdr_common *hdr,
+						struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_short *data_block_hdr =
+			(struct ma_usb_hdr_isochdatablock_short *)
+			shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+				  opt_hdr_shift);
+	u8 *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers *
+				   sizeof(*data_block_hdr));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = urb_ctx->urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			     event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num  = data_block_hdr[i].segment_number;
+		u16 seg_size = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, urb.number_of_packets=%d",
+				     event->data.ep_handle, seg_num,
+				     urb->number_of_packets);
+			break;
+		}
+
+		if (seg_size > urb->iso_frame_desc[seg_num].length) {
+			mausb_pr_err("Block to long for segment: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, seg_size) > end_of_packet) {
+			mausb_pr_err("End of segment after enf of packet: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset);
+		mausb_data_iterator_write(&urb_ctx->iterator, isoch_data,
+					  seg_size);
+
+		isoch_data = shift_ptr(isoch_data, seg_size);
+
+		urb->iso_frame_desc[seg_num].actual_length = seg_size;
+		urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+static void __mausb_process_in_isoch_std_resp(struct mausb_event *event,
+					      struct ma_usb_hdr_common *hdr,
+					      struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_std *data_block_hdr =
+		(struct ma_usb_hdr_isochdatablock_std *)
+		shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+			  opt_hdr_shift);
+	u8 *isoch_data =
+		shift_ptr(data_block_hdr, hdr->data.headers *
+			  sizeof(struct ma_usb_hdr_isochdatablock_std));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = (struct urb *)event->data.urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			     event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num   = data_block_hdr[i].segment_number;
+		u16 seg_len   = data_block_hdr[i].segment_length;
+		u16 block_len = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, number_of_packets=%d",
+				     event->data.ep_handle, seg_num,
+				     urb->number_of_packets);
+			break;
+		}
+
+		if (block_len > urb->iso_frame_desc[seg_num].length -
+			     urb->iso_frame_desc[seg_num].actual_length) {
+			mausb_pr_err("Block too long for segment: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, block_len) >
+				       end_of_packet) {
+			mausb_pr_err("End of fragment after end of packet: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset +
+					 data_block_hdr[i].fragment_offset);
+		mausb_data_iterator_write(&urb_ctx->iterator,
+					  isoch_data, block_len);
+		isoch_data = shift_ptr(isoch_data, block_len);
+
+		urb->iso_frame_desc[seg_num].actual_length += block_len;
+
+		if (urb->iso_frame_desc[seg_num].actual_length == seg_len)
+			urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+void mausb_receive_isoch_in_data(struct mausb_device *dev,
+				 struct mausb_event *event,
+				 struct mausb_urb_ctx *urb_ctx)
+{
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	struct ma_usb_hdr_transfer *transfer_hdr =
+					mausb_get_data_transfer_hdr(common_hdr);
+
+	if (!(common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK)) {
+		/* Short ISO headers response */
+		__mausb_process_in_isoch_short_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_STD) {
+		/* Standard ISO headers response */
+		__mausb_process_in_isoch_std_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_LONG) {
+		/* Long ISO headers response */
+		mausb_pr_warn("Long isoc headers in response: ep_handle=%#x, req_id=%#x",
+			      event->data.ep_handle, transfer_hdr->req_id);
+	} else {
+		/* Error */
+		mausb_pr_err("Isoc header error in response: ep_handle=%#x, req_id=%#x",
+			     event->data.ep_handle, transfer_hdr->req_id);
+	}
+}
+
+static inline u32
+__mausb_calculate_isoch_common_header_size(u32 num_of_segments)
+{
+	return MAUSB_ISOCH_TRANSFER_HDR_SIZE +
+			MAUSB_ISOCH_STANDARD_FORMAT_SIZE * num_of_segments;
+}
+
+static struct ma_usb_hdr_common *
+__mausb_create_isoch_out_transfer_packet(struct mausb_event *event,
+					 struct mausb_urb_ctx *urb_ctx,
+					 u16 payload_size, u32 seq_n,
+					 u32 start_of_segments,
+					 u32 number_of_segments)
+{
+	struct ma_usb_hdr_common		 *hdr;
+	struct ma_usb_hdr_isochtransfer		 *hdr_isochtransfer;
+	struct ma_usb_hdr_isochdatablock_std	 *isoc_header_std;
+	struct ma_usb_hdr_isochtransfer_optional *hdr_opt_isochtransfer;
+	struct urb *urb = (struct urb *)event->data.urb;
+	void *isoc_headers = NULL;
+	u32 length;
+	u16 i;
+	unsigned long block_length;
+	u32 number_of_packets = (u32)event->data.isoch_seg_num;
+	u32 size_of_request =
+		__mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr = kzalloc(size_of_request, GFP_KERNEL);
+	if (!hdr)
+		return NULL;
+
+	hdr->version	  = MA_USB_HDR_VERSION_1_0;
+	hdr->ssid	  = event->data.mausb_ssid;
+	hdr->flags	  = MA_USB_HDR_FLAGS_HOST;
+	hdr->dev_addr	  = event->data.mausb_address;
+	hdr->handle.epv	  = event->data.ep_handle;
+	hdr->data.status  = MA_USB_HDR_STATUS_NO_ERROR;
+	hdr->data.eps	  = MAUSB_TRANSFER_RESERVED;
+	hdr->data.t_flags = (u8)(usb_endpoint_type(&urb->ep->desc) << 3);
+
+	isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE);
+
+	for (i = (u16)start_of_segments;
+	     i < number_of_segments + start_of_segments; ++i) {
+		block_length = i < number_of_packets - 1 ?
+			urb->iso_frame_desc[i + 1].offset -
+			urb->iso_frame_desc[i].offset :
+			mausb_data_iterator_length(&urb_ctx->iterator) -
+			urb->iso_frame_desc[i].offset;
+
+		urb->iso_frame_desc[i].status = MA_USB_HDR_STATUS_UNSUCCESSFUL;
+		isoc_header_std = (struct ma_usb_hdr_isochdatablock_std *)
+			shift_ptr(isoc_headers,
+				  (u64)MAUSB_ISOCH_STANDARD_FORMAT_SIZE *
+				  (i - start_of_segments));
+		isoc_header_std->block_length	 = (u16)block_length;
+		isoc_header_std->segment_number  = i;
+		isoc_header_std->s_flags	 = 0;
+		isoc_header_std->segment_length  = (u16)block_length;
+		isoc_header_std->fragment_offset = 0;
+	}
+
+	length = __mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr->flags		|= MA_USB_HDR_FLAGS_TIMESTAMP;
+	hdr->type		 = (u8)MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER);
+	hdr->data.headers	 = (u16)number_of_segments;
+	hdr->data.i_flags	 = MA_USB_DATA_IFLAGS_HDR_FMT_STD |
+				      MA_USB_DATA_IFLAGS_ASAP;
+	hdr_opt_isochtransfer	    = mausb_hdr_isochtransfer_optional_hdr(hdr);
+	hdr_isochtransfer	    = mausb_get_isochtransfer_hdr(hdr);
+	hdr_isochtransfer->req_id   = event->data.req_id;
+	hdr_isochtransfer->seq_n    = seq_n;
+	hdr_isochtransfer->segments = number_of_packets;
+
+	hdr_isochtransfer->presentation_time = MA_USB_TRANSFER_RESERVED;
+
+	hdr_opt_isochtransfer->timestamp = MA_USB_TRANSFER_RESERVED;
+	hdr_opt_isochtransfer->mtd	 = MA_USB_TRANSFER_RESERVED;
+
+	hdr->length = (u16)length + payload_size;
+
+	return hdr;
+}
+
+static int
+mausb_init_isoch_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				  struct list_head *chunks_list,
+				  u32 *num_of_data_chunks,
+				  u32 num_of_packets)
+{
+	u32 header_size =
+		__mausb_calculate_isoch_common_header_size(num_of_packets);
+	int status = mausb_add_data_chunk(common_hdr, header_size, chunks_list);
+
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static
+int mausb_prepare_isoch_out_transfer_packet(struct ma_usb_hdr_common *hdr,
+					    struct mausb_event *event,
+					    struct mausb_urb_ctx *urb_ctx,
+					    struct mausb_kvec_data_wrapper *
+					    result_data_wrapper)
+{
+	u32 num_of_data_chunks	       = 0;
+	u32 num_of_payload_data_chunks = 0;
+	u32 segment_number	       = event->data.isoch_seg_num;
+	u32 payload_data_size;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	int status = 0;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_isoch_out_header_chunk(hdr, &chunks_list,
+					      &num_of_data_chunks,
+					      segment_number) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size = hdr->length -
+		__mausb_calculate_isoch_common_header_size(segment_number);
+
+	if (mausb_data_iterator_read(&urb_ctx->iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		mausb_pr_err("Data iterator read failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(result_data_wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		mausb_pr_err("Data wrapper init failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks:
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+static int mausb_create_and_send_isoch_transfer_req(struct mausb_device *dev,
+						    struct mausb_event *event,
+						    struct mausb_urb_ctx
+						    *urb_ctx, u32 *seq_n,
+						    u32 payload_size,
+						    u32 start_of_segments,
+						    u32 number_of_segments)
+{
+	struct ma_usb_hdr_common *hdr;
+	struct mausb_kvec_data_wrapper data_to_send;
+	int status;
+	enum mausb_channel channel;
+
+	hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx,
+						       (u16)payload_size,
+						       *seq_n,
+						       start_of_segments,
+						       number_of_segments);
+	if (!hdr) {
+		mausb_pr_alert("Isoch transfer packet alloc failed");
+		return -ENOMEM;
+	}
+	*seq_n = (*seq_n + 1) % (MA_USB_TRANSFER_SEQN_MAX + 1);
+
+	status = mausb_prepare_isoch_out_transfer_packet(hdr, event, urb_ctx,
+							 &data_to_send);
+	if (status < 0) {
+		mausb_pr_alert("Failed to prepare transfer packet");
+		kfree(hdr);
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data_to_send);
+
+	kfree(hdr);
+	kfree(data_to_send.kvec);
+
+	return status;
+}
+
+static inline int __mausb_send_isoch_out_packet(struct mausb_device *dev,
+						struct mausb_event *event,
+						struct mausb_urb_ctx *urb_ctx,
+						u32 *seq_n,
+						u32 *starting_segments,
+						u32 *rem_transfer_buf,
+						u32 *payload_size, u32 index)
+{
+	int status = mausb_create_and_send_isoch_transfer_req(dev, event,
+					urb_ctx, seq_n, *payload_size,
+					*starting_segments,
+					index - *starting_segments);
+	if (status < 0) {
+		mausb_pr_err("ISOCH transfer request create and send failed");
+		return status;
+	}
+	*starting_segments = index;
+	*rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	*payload_size	   = 0;
+
+	return 0;
+}
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx)
+{
+	u32   starting_segments = 0;
+	u32   rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	struct urb *urb = (struct urb *)mausb_event->data.urb;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+	u32 payload_size   = 0;
+	u32 chunk_size;
+	u32 seq_n	   = 0;
+	int status;
+	u32 i;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		if (i < number_of_packets - 1)
+			chunk_size = urb->iso_frame_desc[i + 1].offset -
+					urb->iso_frame_desc[i].offset;
+		else
+			chunk_size =
+				mausb_data_iterator_length(&urb_ctx->iterator) -
+						urb->iso_frame_desc[i].offset;
+
+		if (chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE >
+		    rem_transfer_buf) {
+			if (payload_size == 0) {
+				mausb_pr_warn("Fragmentation");
+			} else {
+				status = __mausb_send_isoch_out_packet
+						(ma_dev, mausb_event, urb_ctx,
+						 &seq_n, &starting_segments,
+						 &rem_transfer_buf,
+						 &payload_size, i);
+				if (status < 0)
+					return status;
+				i--;
+				continue;
+			}
+		} else {
+			rem_transfer_buf -=
+				chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE;
+			payload_size += chunk_size;
+		}
+
+		if (i == number_of_packets - 1 || rem_transfer_buf == 0) {
+			status = __mausb_send_isoch_out_packet
+					(ma_dev, mausb_event, urb_ctx, &seq_n,
+					 &starting_segments, &rem_transfer_buf,
+					 &payload_size, i + 1);
+			if (status < 0)
+				return status;
+		}
+	}
+	return 0;
+}
+
+void mausb_receive_isoch_out(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	u16 i;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size, event->data.rem_transfer_size,
+		       event->status);
+
+	for (i = 0; i < urb->number_of_packets; ++i)
+		urb->iso_frame_desc[i].status = event->status;
+
+	mausb_complete_request(urb, event->data.payload_size, event->status);
+}
diff --git a/drivers/usb/mausb_host/hpal_data.h b/drivers/usb/mausb_host/hpal_data.h
new file mode 100644
index 000000000000..4b351d508966
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_data.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_DATA_H__
+#define __MAUSB_HPAL_DATA_H__
+
+#include <linux/types.h>
+
+#include "hpal_events.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event);
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+
+#define MAUSB_ISOCH_IN_KVEC_NUM 3
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev,
+			    struct mausb_event *event);
+void mausb_receive_isoch_in_data(struct mausb_device *dev,
+				 struct mausb_event *event,
+				 struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx);
+void mausb_receive_isoch_out(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_DATA_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-25  9:19     ` [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
@ 2020-04-26  0:32       ` Alan Stern
  2020-04-26 12:32         ` Vladimir Stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Alan Stern @ 2020-04-26  0:32 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:

> Protocol adaptation layer (PAL) implementation has been added to
> introduce MA-USB structures and logic.
> 
> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>

...

> +	/*
> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
> +	 * should not, so it is breaking the USB drive on the linux
> +	 */
> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;

Removing the SHORT_NOT_OK flag is _not_ a valid thing to do.  It will 
cause drivers to malfunction.

Can you please explain this comment?

	What SCSI driver?

	When is the flag being added?

	How does it break USB drives?

	Why haven't you already reported this problem to the 
	appropriate maintainers?

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-26  0:32       ` Alan Stern
@ 2020-04-26 12:32         ` Vladimir Stankovic
  2020-04-26 14:31           ` Alan Stern
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-04-26 12:32 UTC (permalink / raw)
  To: Alan Stern; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On 26.4.20. 02:32, Alan Stern wrote:
> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
> 
>> Protocol adaptation layer (PAL) implementation has been added to
>> introduce MA-USB structures and logic.
>>
>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> 
> ...
> 
>> +	/*
>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
>> +	 * should not, so it is breaking the USB drive on the linux
>> +	 */
>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
> 
> Removing the SHORT_NOT_OK flag is _not_ a valid thing to do.  It will 
> cause drivers to malfunction.
> 
> Can you please explain this comment?
> 
> 	What SCSI driver?
> 
> 	When is the flag being added?
> 
> 	How does it break USB drives?
> 
> 	Why haven't you already reported this problem to the 
> 	appropriate maintainers?
> 
> Alan Stern
> 

Hi,

Issue that removal of SHORT_NOT_OK flag addressed is linked to particular
set of Kingston USB 3.0 flash drives (super speed) - other USB flash drives
haven't had this flag set. Without this "fix", those Kingston flash drives
are not being enumerated properly.

This particular line was added in the early stage of development, during
enumeration process implementation. The reason why it remained in the code
since is because we haven't noticed any side-effects, even with various
USB devices being attached to remote MA-USB device, including flash drives,
cameras, wireless mice, etc.

The problem has been reported, and is actively being investigated.

As soon as it gets addressed properly (w/o global negation of the flag),
a new patch will be pushed.

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-26 12:32         ` Vladimir Stankovic
@ 2020-04-26 14:31           ` Alan Stern
  2020-04-26 14:45             ` [External] " Vladimir Stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Alan Stern @ 2020-04-26 14:31 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Sun, 26 Apr 2020, Vladimir Stankovic wrote:

> On 26.4.20. 02:32, Alan Stern wrote:
> > On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
> > 
> >> Protocol adaptation layer (PAL) implementation has been added to
> >> introduce MA-USB structures and logic.
> >>
> >> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> > 
> > ...
> > 
> >> +	/*
> >> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
> >> +	 * should not, so it is breaking the USB drive on the linux
> >> +	 */
> >> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
> > 
> > Removing the SHORT_NOT_OK flag is _not_ a valid thing to do.  It will 
> > cause drivers to malfunction.
> > 
> > Can you please explain this comment?
> > 
> > 	What SCSI driver?
> > 
> > 	When is the flag being added?
> > 
> > 	How does it break USB drives?
> > 
> > 	Why haven't you already reported this problem to the 
> > 	appropriate maintainers?
> > 
> > Alan Stern
> > 
> 
> Hi,
> 
> Issue that removal of SHORT_NOT_OK flag addressed is linked to particular
> set of Kingston USB 3.0 flash drives (super speed) - other USB flash drives
> haven't had this flag set. Without this "fix", those Kingston flash drives
> are not being enumerated properly.

Please explain in detail how the enumeration of these Kingston flash
drives fails.  Or if such an explanation has already been posted,
please provide a link to it.

> This particular line was added in the early stage of development, during
> enumeration process implementation. The reason why it remained in the code
> since is because we haven't noticed any side-effects, even with various
> USB devices being attached to remote MA-USB device, including flash drives,
> cameras, wireless mice, etc.

Come to think of it, the SHORT_NOT_OK flag is mainly used with HCDs
that don't have scatter-gather support.  Since your mausb driver does
support scatter-gather, you most likely won't encounter any problems 
unless you go looking for them specifically.

> The problem has been reported, and is actively being investigated.

Where was the problem reported (URL to a mailing list archive)?  Who is
investigating it?

> As soon as it gets addressed properly (w/o global negation of the flag),
> a new patch will be pushed.

Thank you.

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-26 14:31           ` Alan Stern
@ 2020-04-26 14:45             ` Vladimir Stankovic
  2020-04-26 20:56               ` Alan Stern
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-04-26 14:45 UTC (permalink / raw)
  To: Alan Stern; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On 26.4.20. 16:31, Alan Stern wrote:
> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> 
>> On 26.4.20. 02:32, Alan Stern wrote:
>>> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
>>>
>>>> Protocol adaptation layer (PAL) implementation has been added to
>>>> introduce MA-USB structures and logic.
>>>>
>>>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
>>>
>>> ...
>>>
>>>> +	/*
>>>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
>>>> +	 * should not, so it is breaking the USB drive on the linux
>>>> +	 */
>>>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
>>>
>>> Removing the SHORT_NOT_OK flag is _not_ a valid thing to do.  It will 
>>> cause drivers to malfunction.
>>>
>>> Can you please explain this comment?
>>>
>>> 	What SCSI driver?
>>>
>>> 	When is the flag being added?
>>>
>>> 	How does it break USB drives?
>>>
>>> 	Why haven't you already reported this problem to the 
>>> 	appropriate maintainers?
>>>
>>> Alan Stern
>>>
>>
>> Hi,
>>
>> Issue that removal of SHORT_NOT_OK flag addressed is linked to particular
>> set of Kingston USB 3.0 flash drives (super speed) - other USB flash drives
>> haven't had this flag set. Without this "fix", those Kingston flash drives
>> are not being enumerated properly.
> 
> Please explain in detail how the enumeration of these Kingston flash
> drives fails.  Or if such an explanation has already been posted,
> please provide a link to it.

Will reproduce the issue once again (w/o the fix) and run through the events.
Issue has been noticed during early development, and addressed right away.
> 
>> This particular line was added in the early stage of development, during
>> enumeration process implementation. The reason why it remained in the code
>> since is because we haven't noticed any side-effects, even with various
>> USB devices being attached to remote MA-USB device, including flash drives,
>> cameras, wireless mice, etc.
> 
> Come to think of it, the SHORT_NOT_OK flag is mainly used with HCDs
> that don't have scatter-gather support.  Since your mausb driver does
> support scatter-gather, you most likely won't encounter any problems 
> unless you go looking for them specifically.
> 
>> The problem has been reported, and is actively being investigated.
> 
> Where was the problem reported (URL to a mailing list archive)?  Who is
> investigating it?

Ticket has been submitted to DisplayLink's internal issue-tracking system
and is being investigated by mausb-host-devel team.
> 
>> As soon as it gets addressed properly (w/o global negation of the flag),
>> a new patch will be pushed.
> 
> Thank you.
> 
> Alan Stern
> 


-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-26 14:45             ` [External] " Vladimir Stankovic
@ 2020-04-26 20:56               ` Alan Stern
  2020-04-30 14:37                 ` Vladimir Stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Alan Stern @ 2020-04-26 20:56 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Sun, 26 Apr 2020, Vladimir Stankovic wrote:

> On 26.4.20. 16:31, Alan Stern wrote:
> > On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> > 
> >> On 26.4.20. 02:32, Alan Stern wrote:
> >>> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
> >>>
> >>>> Protocol adaptation layer (PAL) implementation has been added to
> >>>> introduce MA-USB structures and logic.
> >>>>
> >>>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> >>>
> >>> ...
> >>>
> >>>> +	/*
> >>>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
> >>>> +	 * should not, so it is breaking the USB drive on the linux
> >>>> +	 */
> >>>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
> >>>
> >>> Removing the SHORT_NOT_OK flag is _not_ a valid thing to do.  It will 
> >>> cause drivers to malfunction.
> >>>
> >>> Can you please explain this comment?
> >>>
> >>> 	What SCSI driver?
> >>>
> >>> 	When is the flag being added?
> >>>
> >>> 	How does it break USB drives?
> >>>
> >>> 	Why haven't you already reported this problem to the 
> >>> 	appropriate maintainers?
> >>>
> >>> Alan Stern
> >>>
> >>
> >> Hi,
> >>
> >> Issue that removal of SHORT_NOT_OK flag addressed is linked to particular
> >> set of Kingston USB 3.0 flash drives (super speed) - other USB flash drives
> >> haven't had this flag set. Without this "fix", those Kingston flash drives
> >> are not being enumerated properly.
> > 
> > Please explain in detail how the enumeration of these Kingston flash
> > drives fails.  Or if such an explanation has already been posted,
> > please provide a link to it.
> 
> Will reproduce the issue once again (w/o the fix) and run through the events.
> Issue has been noticed during early development, and addressed right away.
> > 
> >> This particular line was added in the early stage of development, during
> >> enumeration process implementation. The reason why it remained in the code
> >> since is because we haven't noticed any side-effects, even with various
> >> USB devices being attached to remote MA-USB device, including flash drives,
> >> cameras, wireless mice, etc.
> > 
> > Come to think of it, the SHORT_NOT_OK flag is mainly used with HCDs
> > that don't have scatter-gather support.  Since your mausb driver does
> > support scatter-gather, you most likely won't encounter any problems 
> > unless you go looking for them specifically.
> > 
> >> The problem has been reported, and is actively being investigated.
> > 
> > Where was the problem reported (URL to a mailing list archive)?  Who is
> > investigating it?
> 
> Ticket has been submitted to DisplayLink's internal issue-tracking system
> and is being investigated by mausb-host-devel team.

Okay.  What SCSI driver does the comment refer to?  Is it something 
internal to DisplayLink or is it part of the regular Linux kernel?

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 1/8] usb: Add MA-USB Host kernel module
  2020-04-25  9:19     ` [PATCH v5 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
@ 2020-04-28 11:03       ` Greg KH
  0 siblings, 0 replies; 62+ messages in thread
From: Greg KH @ 2020-04-28 11:03 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Sat, Apr 25, 2020 at 11:19:47AM +0200, vladimir.stankovic@displaylink.com wrote:
> Added utility macros, kernel device creation and cleanup, functions for
> handling log formatting and a placeholder module for MA-USB Host device
> driver.
> 
> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> ---
>  MAINTAINERS                         |  7 +++
>  drivers/usb/Kconfig                 |  2 +
>  drivers/usb/Makefile                |  2 +
>  drivers/usb/mausb_host/Kconfig      | 15 +++++
>  drivers/usb/mausb_host/Makefile     | 12 ++++
>  drivers/usb/mausb_host/mausb_core.c | 90 +++++++++++++++++++++++++++++
>  drivers/usb/mausb_host/utils.c      | 85 +++++++++++++++++++++++++++
>  drivers/usb/mausb_host/utils.h      | 40 +++++++++++++
>  8 files changed, 253 insertions(+)
>  create mode 100644 drivers/usb/mausb_host/Kconfig
>  create mode 100644 drivers/usb/mausb_host/Makefile
>  create mode 100644 drivers/usb/mausb_host/mausb_core.c
>  create mode 100644 drivers/usb/mausb_host/utils.c
>  create mode 100644 drivers/usb/mausb_host/utils.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 453fe0713e68..8b63b246ba67 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10352,6 +10352,13 @@ W:	https://linuxtv.org
>  T:	git git://linuxtv.org/media_tree.git
>  F:	drivers/media/radio/radio-maxiradio*
>  
> +MEDIA AGNOSTIC (MA) USB HOST DRIVER
> +M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> +L:	mausb-host-devel@displaylink.com
> +W:	https://www.displaylink.com
> +S:	Maintained
> +F:	drivers/usb/mausb_host/*
> +
>  MCAN MMIO DEVICE DRIVER
>  M:	Dan Murphy <dmurphy@ti.com>
>  M:	Sriram Dash <sriram.dash@samsung.com>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index 275568abc670..4e92f1fa0fa5 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -164,6 +164,8 @@ source "drivers/usb/misc/Kconfig"
>  
>  source "drivers/usb/atm/Kconfig"
>  
> +source "drivers/usb/mausb_host/Kconfig"
> +
>  endif # USB
>  
>  source "drivers/usb/phy/Kconfig"
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index 1c1c1d659394..22d1998db0e2 100644
> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -66,3 +66,5 @@ obj-$(CONFIG_USBIP_CORE)	+= usbip/
>  obj-$(CONFIG_TYPEC)		+= typec/
>  
>  obj-$(CONFIG_USB_ROLE_SWITCH)	+= roles/
> +
> +obj-$(CONFIG_HOST_MAUSB)        += mausb_host/
> diff --git a/drivers/usb/mausb_host/Kconfig b/drivers/usb/mausb_host/Kconfig
> new file mode 100644
> index 000000000000..a8363e7e8f97
> --- /dev/null
> +++ b/drivers/usb/mausb_host/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Kernel configuration file for MA-USB Host driver.
> +#
> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> +#
> +
> +config HOST_MAUSB
> +	tristate "Media Agnostic (MA) USB Host Driver"
> +	depends on USB=y

Why can't USB=m?

> +	help
> +	  This is a Media Agnostic (MA) USB Host driver which enables host
> +	  communication via MA USB protocol stack.
> +
> +	  If this driver is compiled as a module, it will be named mausb_host.

And why isn't this all in drivers/usb/host/ ?  Why a separate directory?

If you really need your own directory, why not drivers/usb/host/mausb/ ?



> diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
> new file mode 100644
> index 000000000000..2e353fa0958b
> --- /dev/null
> +++ b/drivers/usb/mausb_host/Makefile
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for DisplayLink MA-USB Host driver.
> +#
> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> +#
> +
> +obj-$(CONFIG_HOST_MAUSB) += mausb_host.o
> +mausb_host-y := mausb_core.o
> +mausb_host-y += utils.o
> +
> +ccflags-y += -I$(srctree)/$(src)

Ick, really?  Why?  You should not need this.



> diff --git a/drivers/usb/mausb_host/mausb_core.c b/drivers/usb/mausb_host/mausb_core.c
> new file mode 100644
> index 000000000000..8638dd0a4856
> --- /dev/null
> +++ b/drivers/usb/mausb_host/mausb_core.c
> @@ -0,0 +1,90 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> + */
> +#include <linux/in.h>
> +#include <linux/inet.h>

Why do you need these two .h files for this file at this time?

> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/kobject.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>

You have no module parameters here, why do you need this?

> +#include <linux/net.h>

Why do you need this?

> +
> +#include "utils.h"
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("DisplayLink (UK) Ltd.");
> +MODULE_VERSION(MAUSB_DRIVER_VERSION);
> +
> +static int mausb_client_connect(const char *value,
> +				const struct kernel_param *kp)
> +{
> +	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);

No custom driver "printk" macros please.  We have been stomping them out
for years.  Just use dev_*() instead.

And a driver version means nothing when it is in the kernel itself,
please just remove.

> +
> +	return 0;
> +}
> +
> +static int mausb_client_disconnect(const char *value,
> +				   const struct kernel_param *kp)
> +{
> +	mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION);

Also, why info?  This is just fun debugging stuff, don't do that, that
is what ftrace is for.

If you are trying to provide stubs to later fill in, that's fine, but
don't clutter it up with this type of stuff please.

> +int mausb_create_dev(void)
> +{
> +	int device_created = 0;
> +	int status = alloc_chrdev_region(&mausb_major_kernel, 0, 1,
> +					 MAUSB_KERNEL_DEV_NAME "_proc");

Why does a USB host driver need a char dev node?

> +	if (status)
> +		goto cleanup;
> +
> +	mausb_kernel_class = class_create(THIS_MODULE,
> +					  MAUSB_KERNEL_DEV_NAME "_sys");

Why do you need your own class that has a different name from your
device node?  None of that should be needed at all here, right?

> +	if (!mausb_kernel_class) {
> +		status = -ENOMEM;
> +		goto cleanup;
> +	}
> +
> +	if (!device_create(mausb_kernel_class, NULL, mausb_major_kernel, NULL,
> +			   MAUSB_KERNEL_DEV_NAME "_dev")) {
> +		status = -ENOMEM;
> +		goto cleanup;
> +	}
> +	device_created = 1;

You set this and never touch it again :(
Oh wait, you pass it into a cleanup function later, which isn't really
needed anyway.

> +	cdev_init(&mausb_kernel_dev, &mausb_file_ops);
> +	status = cdev_add(&mausb_kernel_dev, mausb_major_kernel, 1);

one device node?  If you REALLY need it, just use a misc device, but we
need a ton of documentation here as to what you are doing with this, and
why it is needed, because as-is, I have no idea just looking at this
patch :(


> +	if (status)
> +		goto cleanup;
> +	return 0;
> +cleanup:
> +	mausb_cleanup_dev(device_created);
> +	return status;
> +}
> +
> +void mausb_cleanup_dev(int device_created)
> +{
> +	if (device_created) {

So this isn't a global variable??

That really isn't needed, please don't.

> +		device_destroy(mausb_kernel_class, mausb_major_kernel);
> +		cdev_del(&mausb_kernel_dev);
> +	}
> +
> +	if (mausb_kernel_class)
> +		class_destroy(mausb_kernel_class);
> +
> +	unregister_chrdev_region(mausb_major_kernel, 1);
> +}
> diff --git a/drivers/usb/mausb_host/utils.h b/drivers/usb/mausb_host/utils.h
> new file mode 100644
> index 000000000000..9adf4122e64d
> --- /dev/null
> +++ b/drivers/usb/mausb_host/utils.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> + */
> +#ifndef __MAUSB_UTILS_H__
> +#define __MAUSB_UTILS_H__
> +
> +#if defined(MAUSB_NO_LOGS)
> +#define mausb_pr_logs(...)
> +#else
> +#include <linux/printk.h>
> +#include <linux/sched.h>
> +#define mausb_pr_logs(level_str, level, log, ...)\
> +	pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\
> +	current->pid, current->tgid, __func__, ##__VA_ARGS__)
> +#endif /* MAUSB_NO_LOGS */
> +
> +#define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__)
> +
> +#define mausb_pr_err(...) mausb_pr_logs(err, 2, ##__VA_ARGS__)
> +
> +#define mausb_pr_warn(...) mausb_pr_logs(warn, 3, ##__VA_ARGS__)
> +
> +#define mausb_pr_info(...) mausb_pr_logs(info, 4, ##__VA_ARGS__)
> +
> +#if defined(MAUSB_LOG_VERBOSE)
> +	#define mausb_pr_debug(...) mausb_pr_logs(debug, 5, ##__VA_ARGS__)
> +#else
> +	#define mausb_pr_debug(...)
> +#endif /* defined(MAUSB_LOG_VERBOSE) */

Again, drop all of this, and use the build-in kernel functions, there's
nothing special about this one tiny driver to override uniformity.

> +
> +#define MAUSB_STRINGIFY2(x) #x
> +#define MAUSB_STRINGIFY(x) MAUSB_STRINGIFY2(x)

Ick, why???

> +
> +#define MAUSB_DRIVER_VERSION MAUSB_STRINGIFY(1.3.0.0.6f5beb53)

That's funny.  And pointless :)

> +
> +int mausb_create_dev(void);
> +void mausb_cleanup_dev(int device_created);

No need for that parameter.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 0/8] Add MA USB Host driver
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (7 preceding siblings ...)
  2020-04-25  9:19     ` [PATCH v5 8/8] usb: mausb_host: Process MA-USB data packets vladimir.stankovic
@ 2020-04-28 11:04     ` Greg KH
  2020-04-30 16:51       ` [External] " Vladimir Stankovic
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
  9 siblings, 1 reply; 62+ messages in thread
From: Greg KH @ 2020-04-28 11:04 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Sat, Apr 25, 2020 at 11:19:46AM +0200, vladimir.stankovic@displaylink.com wrote:
> Media Agnostic (MA) USB Host driver provides USB connectivity over an
> available network, allowing host device to access remote USB devices
> attached to one or more MA USB devices (accessible via network).
> 
> This driver has been developed to enable the host to communicate
> with DisplayLink products supporting MA USB protocol (MA USB device,
> in terms of MA USB Specification).
> 
> MA USB protocol used by MA USB Host driver has been implemented in
> accordance with MA USB Specification Release 1.0b.

Is that a USB-released spec?

> 
> This driver depends on the functions provided by DisplayLink's
> user-space driver.

Where can that userspace code be found?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 3/8] usb: mausb_host: HCD initialization
  2020-04-25  9:19     ` [PATCH v5 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
@ 2020-04-28 11:07       ` Greg KH
  0 siblings, 0 replies; 62+ messages in thread
From: Greg KH @ 2020-04-28 11:07 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Sat, Apr 25, 2020 at 11:19:49AM +0200, vladimir.stankovic@displaylink.com wrote:
> +static long mausb_ioctl(struct file *file, unsigned int ioctl_func,
> +			unsigned long ioctl_buffer)
> +{
> +	return 0;
> +}
> +

Why do you have an ioctl that does nothing?  Same for the other
functions that just return "success", if you don't do anything in them,
don't declare them please.



> +int mausb_init_hcd(void)
> +{
> +	int retval;
> +
> +	retval = register_chrdev(0, DEVICE_NAME, &mausb_fops);

But you already have a chardev?  Why use another one?

I don't understand the need for these, it's not obvious at all, please
provide a lot more information on what is going on with these userspace
facing interfaces please.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing
  2020-04-25  9:19     ` [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
@ 2020-04-28 11:08       ` Greg KH
  0 siblings, 0 replies; 62+ messages in thread
From: Greg KH @ 2020-04-28 11:08 UTC (permalink / raw)
  To: vladimir.stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Sat, Apr 25, 2020 at 11:19:53AM +0200, vladimir.stankovic@displaylink.com wrote:
> +static int mausb_device_assign_address(struct mausb_device *dev,
> +				       struct mausb_usb_device_ctx *usb_dev_ctx)
> +{
> +	int status =
> +		mausb_setusbdevaddress_event_to_user(dev,
> +						     usb_dev_ctx->dev_handle,
> +						     RESPONSE_TIMEOUT);
> +
> +	mausb_pr_info("dev_handle=%#x, status=%d", usb_dev_ctx->dev_handle,
> +		      status);

When drivers are working properly, they are totally silent.  Please
remove this type of debugging code from the driver entirely, or change
it to be dev_dbg().

Otherwise this really looks like a very noisy driver.

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-26 20:56               ` Alan Stern
@ 2020-04-30 14:37                 ` Vladimir Stankovic
  2020-04-30 15:18                   ` Alan Stern
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-04-30 14:37 UTC (permalink / raw)
  To: Alan Stern; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On 26.4.20. 22:56, Alan Stern wrote:
> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> 
>> On 26.4.20. 16:31, Alan Stern wrote:
>>> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
>>>
>>>> On 26.4.20. 02:32, Alan Stern wrote:
>>>>> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
>>>>>
>>>>>> Protocol adaptation layer (PAL) implementation has been added to
>>>>>> introduce MA-USB structures and logic.
>>>>>>
>>>>>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
>>>>>
>>>>> ...
>>>>>
>>>>>> +	/*
>>>>>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
>>>>>> +	 * should not, so it is breaking the USB drive on the linux
>>>>>> +	 */
>>>>>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
>>>>>
>>>>> Removing the SHORT_NOT_OK flag is _not_ a valid thing to do.  It will 
>>>>> cause drivers to malfunction.
>>>>>
>>>>> Can you please explain this comment?
>>>>>
>>>>> 	What SCSI driver?
>>>>>
>>>>> 	When is the flag being added?
>>>>>
>>>>> 	How does it break USB drives?
>>>>>
>>>>> 	Why haven't you already reported this problem to the 
>>>>> 	appropriate maintainers?
>>>>>
>>>>> Alan Stern
>>>>>
>>>>
>>>> Hi,
>>>>
>>>> Issue that removal of SHORT_NOT_OK flag addressed is linked to particular
>>>> set of Kingston USB 3.0 flash drives (super speed) - other USB flash drives
>>>> haven't had this flag set. Without this "fix", those Kingston flash drives
>>>> are not being enumerated properly.
>>>
>>> Please explain in detail how the enumeration of these Kingston flash
>>> drives fails.  Or if such an explanation has already been posted,
>>> please provide a link to it.
>>
>> Will reproduce the issue once again (w/o the fix) and run through the events.
>> Issue has been noticed during early development, and addressed right away.
>>>
>>>> This particular line was added in the early stage of development, during
>>>> enumeration process implementation. The reason why it remained in the code
>>>> since is because we haven't noticed any side-effects, even with various
>>>> USB devices being attached to remote MA-USB device, including flash drives,
>>>> cameras, wireless mice, etc.
>>>
>>> Come to think of it, the SHORT_NOT_OK flag is mainly used with HCDs
>>> that don't have scatter-gather support.  Since your mausb driver does
>>> support scatter-gather, you most likely won't encounter any problems 
>>> unless you go looking for them specifically.
>>>
>>>> The problem has been reported, and is actively being investigated.
>>>
>>> Where was the problem reported (URL to a mailing list archive)?  Who is
>>> investigating it?
>>
>> Ticket has been submitted to DisplayLink's internal issue-tracking system
>> and is being investigated by mausb-host-devel team.
> 
> Okay.  What SCSI driver does the comment refer to?  Is it something 
> internal to DisplayLink or is it part of the regular Linux kernel?
> 
> Alan Stern
> 
Hi,

Comment was related to the SCSI driver that's part of regular Linux kernel -
once the remote USB flash drive gets enumerated by host, it would appear as
directly attached to host and then handled by the kernel.

With current implementation, following messages are being logged:

scsi 3:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
sd 3:0:0:0: Attached scsi generic sg2 type 0

after which the flash drive is usable/accessible from host side.

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-30 14:37                 ` Vladimir Stankovic
@ 2020-04-30 15:18                   ` Alan Stern
  2020-04-30 15:34                     ` Vladimir Stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Alan Stern @ 2020-04-30 15:18 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Thu, 30 Apr 2020, Vladimir Stankovic wrote:

> On 26.4.20. 22:56, Alan Stern wrote:
> > On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> > 
> >> On 26.4.20. 16:31, Alan Stern wrote:
> >>> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> >>>
> >>>> On 26.4.20. 02:32, Alan Stern wrote:
> >>>>> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
> >>>>>
> >>>>>> Protocol adaptation layer (PAL) implementation has been added to
> >>>>>> introduce MA-USB structures and logic.
> >>>>>>
> >>>>>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> >>>>>
> >>>>> ...
> >>>>>
> >>>>>> +	/*
> >>>>>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
> >>>>>> +	 * should not, so it is breaking the USB drive on the linux
> >>>>>> +	 */
> >>>>>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;

...

> > Okay.  What SCSI driver does the comment refer to?  Is it something 
> > internal to DisplayLink or is it part of the regular Linux kernel?
> > 
> > Alan Stern
> > 
> Hi,
> 
> Comment was related to the SCSI driver that's part of regular Linux kernel -
> once the remote USB flash drive gets enumerated by host, it would appear as
> directly attached to host and then handled by the kernel.
> 
> With current implementation, following messages are being logged:
> 
> scsi 3:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
> sd 3:0:0:0: Attached scsi generic sg2 type 0
> 
> after which the flash drive is usable/accessible from host side.

More context please.  Without the log messages preceding this one we 
can't tell whether the device is using the usb-storage driver or the 
uas driver.

Also, what makes you think the driver is setting the SHORT_NOT_OK flag
at the wrong time?  In fact, how can there be a wrong time?  
SHORT_NOT_OK is a valid flag to use with any control or bulk URB.

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-30 15:18                   ` Alan Stern
@ 2020-04-30 15:34                     ` Vladimir Stankovic
  2020-04-30 15:41                       ` Alan Stern
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-04-30 15:34 UTC (permalink / raw)
  To: Alan Stern; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On 30.4.20. 17:18, Alan Stern wrote:
> On Thu, 30 Apr 2020, Vladimir Stankovic wrote:
> 
>> On 26.4.20. 22:56, Alan Stern wrote:
>>> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
>>>
>>>> On 26.4.20. 16:31, Alan Stern wrote:
>>>>> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
>>>>>
>>>>>> On 26.4.20. 02:32, Alan Stern wrote:
>>>>>>> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
>>>>>>>
>>>>>>>> Protocol adaptation layer (PAL) implementation has been added to
>>>>>>>> introduce MA-USB structures and logic.
>>>>>>>>
>>>>>>>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
>>>>>>>
>>>>>>> ...
>>>>>>>
>>>>>>>> +	/*
>>>>>>>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
>>>>>>>> +	 * should not, so it is breaking the USB drive on the linux
>>>>>>>> +	 */
>>>>>>>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
> 
> ...
> 
>>> Okay.  What SCSI driver does the comment refer to?  Is it something 
>>> internal to DisplayLink or is it part of the regular Linux kernel?
>>>
>>> Alan Stern
>>>
>> Hi,
>>
>> Comment was related to the SCSI driver that's part of regular Linux kernel -
>> once the remote USB flash drive gets enumerated by host, it would appear as
>> directly attached to host and then handled by the kernel.
>>
>> With current implementation, following messages are being logged:
>>
>> scsi 3:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
>> sd 3:0:0:0: Attached scsi generic sg2 type 0
>>
>> after which the flash drive is usable/accessible from host side.
> 
> More context please.  Without the log messages preceding this one we 
> can't tell whether the device is using the usb-storage driver or the 
> uas driver.
> 
> Also, what makes you think the driver is setting the SHORT_NOT_OK flag
> at the wrong time?  In fact, how can there be a wrong time?  
> SHORT_NOT_OK is a valid flag to use with any control or bulk URB.
> 
> Alan Stern
> 
The comment is clearly wrong - as mentioned earlier, this fix was added in early
development phase and I guess that implementer was not clear on how the particular
flag was added. Investigation is ongoing around proper fix for this.

Anyhow, it is a usb-storage driver related to this - here is usb-related log snippet:

usb 3-1.1.2: new high-speed USB device number 5 using mausb_host_hcd_dev
usb 3-1.1.2: New USB device found, idVendor=0951, idProduct=1666
usb 3-1.1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
usb 3-1.1.2: Product: DataTraveler 3.0
usb 3-1.1.2: Manufacturer: Kingston
usb 3-1.1.2: SerialNumber: 0C9D9210E304E311095E087A
usb-storage 3-1.1.2:1.0: USB Mass Storage device detected
scsi host3: usb-storage 3-1.1.2:1.0
scsi 3:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
Attached scsi generic sg2 type 0
[sdb] 30277632 512-byte logical blocks: (15.5 GB/14.4 GiB)

As can be seen, USB flash attached to remote device is properly enumerated via
MA USB. Without the fix, usb driver is not able to read USB descriptors, ending
up in USB storage not being accessible.

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing
  2020-04-30 15:34                     ` Vladimir Stankovic
@ 2020-04-30 15:41                       ` Alan Stern
  0 siblings, 0 replies; 62+ messages in thread
From: Alan Stern @ 2020-04-30 15:41 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: gregkh, linux-kernel, linux-usb, mausb-host-devel

On Thu, 30 Apr 2020, Vladimir Stankovic wrote:

> On 30.4.20. 17:18, Alan Stern wrote:
> > On Thu, 30 Apr 2020, Vladimir Stankovic wrote:
> > 
> >> On 26.4.20. 22:56, Alan Stern wrote:
> >>> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> >>>
> >>>> On 26.4.20. 16:31, Alan Stern wrote:
> >>>>> On Sun, 26 Apr 2020, Vladimir Stankovic wrote:
> >>>>>
> >>>>>> On 26.4.20. 02:32, Alan Stern wrote:
> >>>>>>> On Sat, 25 Apr 2020 vladimir.stankovic@displaylink.com wrote:
> >>>>>>>
> >>>>>>>> Protocol adaptation layer (PAL) implementation has been added to
> >>>>>>>> introduce MA-USB structures and logic.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> >>>>>>>
> >>>>>>> ...
> >>>>>>>
> >>>>>>>> +	/*
> >>>>>>>> +	 * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it
> >>>>>>>> +	 * should not, so it is breaking the USB drive on the linux
> >>>>>>>> +	 */
> >>>>>>>> +	urb->transfer_flags &= ~URB_SHORT_NOT_OK;
> > 
> > ...

> > Also, what makes you think the driver is setting the SHORT_NOT_OK flag
> > at the wrong time?  In fact, how can there be a wrong time?  
> > SHORT_NOT_OK is a valid flag to use with any control or bulk URB.
> > 
> > Alan Stern
> > 
> The comment is clearly wrong - as mentioned earlier, this fix was added in early
> development phase and I guess that implementer was not clear on how the particular
> flag was added. Investigation is ongoing around proper fix for this.
> 
> Anyhow, it is a usb-storage driver related to this - here is usb-related log snippet:
> 
> usb 3-1.1.2: new high-speed USB device number 5 using mausb_host_hcd_dev
> usb 3-1.1.2: New USB device found, idVendor=0951, idProduct=1666
> usb 3-1.1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
> usb 3-1.1.2: Product: DataTraveler 3.0
> usb 3-1.1.2: Manufacturer: Kingston
> usb 3-1.1.2: SerialNumber: 0C9D9210E304E311095E087A
> usb-storage 3-1.1.2:1.0: USB Mass Storage device detected
> scsi host3: usb-storage 3-1.1.2:1.0
> scsi 3:0:0:0: Direct-Access     Kingston DataTraveler 3.0 PMAP PQ: 0 ANSI: 6
> Attached scsi generic sg2 type 0
> [sdb] 30277632 512-byte logical blocks: (15.5 GB/14.4 GiB)
> 
> As can be seen, USB flash attached to remote device is properly enumerated via
> MA USB. Without the fix, usb driver is not able to read USB descriptors, ending
> up in USB storage not being accessible.

That's strange, considering that the SHORT_NOT_OK flag doesn't get set 
when the system is reading the device's USB descriptors.

Alan Stern


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver
  2020-04-28 11:04     ` [PATCH v5 0/8] Add MA USB Host driver Greg KH
@ 2020-04-30 16:51       ` Vladimir Stankovic
  2020-04-30 20:02         ` Greg KH
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-04-30 16:51 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

On 28.4.20. 13:04, Greg KH wrote:
> On Sat, Apr 25, 2020 at 11:19:46AM +0200, vladimir.stankovic@displaylink.com wrote:
>> Media Agnostic (MA) USB Host driver provides USB connectivity over an
>> available network, allowing host device to access remote USB devices
>> attached to one or more MA USB devices (accessible via network).
>>
>> This driver has been developed to enable the host to communicate
>> with DisplayLink products supporting MA USB protocol (MA USB device,
>> in terms of MA USB Specification).
>>
>> MA USB protocol used by MA USB Host driver has been implemented in
>> accordance with MA USB Specification Release 1.0b.
> 
> Is that a USB-released spec?
Correct, document is being maintained by USB IF and is publicly available.
However, I just noticed a typo, correct version is 1.0a. Will correct.

In short, MA USB Specification defines an MA USB protocol that performs USB
communication via any communication medium. As such, it defines how to pack
USB data within MA USB payload, and how to communicate with remote MA USB device.
> 
>>
>> This driver depends on the functions provided by DisplayLink's
>> user-space driver.
> 
> Where can that userspace code be found?
> 
> thanks,
> 
> greg k-h
> 
Userspace code is not publicly available. However, in short, it's purpose is
twofold, to provide interface to application layer, and to prepare MA USB packets
that will be used by remote device.

Related to userspace related questions (i.e. comments around two devices used),
we can provide detailed description of the used IPC. In that sense, please state
the most appropriate way/place to state/publish such description (i.e. is it ok
to add it within the cover letter, or publicly available URL is preferred). 

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver
  2020-04-30 16:51       ` [External] " Vladimir Stankovic
@ 2020-04-30 20:02         ` Greg KH
  2020-05-15 13:04           ` Vladimir Stankovic
  0 siblings, 1 reply; 62+ messages in thread
From: Greg KH @ 2020-04-30 20:02 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Thu, Apr 30, 2020 at 06:51:10PM +0200, Vladimir Stankovic wrote:
> On 28.4.20. 13:04, Greg KH wrote:
> > On Sat, Apr 25, 2020 at 11:19:46AM +0200, vladimir.stankovic@displaylink.com wrote:
> >> Media Agnostic (MA) USB Host driver provides USB connectivity over an
> >> available network, allowing host device to access remote USB devices
> >> attached to one or more MA USB devices (accessible via network).
> >>
> >> This driver has been developed to enable the host to communicate
> >> with DisplayLink products supporting MA USB protocol (MA USB device,
> >> in terms of MA USB Specification).
> >>
> >> MA USB protocol used by MA USB Host driver has been implemented in
> >> accordance with MA USB Specification Release 1.0b.
> > 
> > Is that a USB-released spec?
> Correct, document is being maintained by USB IF and is publicly available.
> However, I just noticed a typo, correct version is 1.0a. Will correct.
> 
> In short, MA USB Specification defines an MA USB protocol that performs USB
> communication via any communication medium. As such, it defines how to pack
> USB data within MA USB payload, and how to communicate with remote MA USB device.
> > 
> >>
> >> This driver depends on the functions provided by DisplayLink's
> >> user-space driver.
> > 
> > Where can that userspace code be found?
> > 
> > thanks,
> > 
> > greg k-h
> > 
> Userspace code is not publicly available. However, in short, it's purpose is
> twofold, to provide interface to application layer, and to prepare MA USB packets
> that will be used by remote device.

So you want us to take a one-off char-driver kernel code for a closed
source userspace application for a public spec?  That feels really
really odd, if not actually against a few licenses.  I hate to ask it,
but are your lawyers ok with this?

> Related to userspace related questions (i.e. comments around two devices used),
> we can provide detailed description of the used IPC. In that sense, please state
> the most appropriate way/place to state/publish such description (i.e. is it ok
> to add it within the cover letter, or publicly available URL is preferred). 

I asked a bunch of questions about this in the patches themselves, you
all need to document the heck out of it everywhere you can, otherwise we
can't even review the code properly.  Could you review it without
knowing what userspace is supposed to be doing?

But, note, I will not take a spec-compliant driver that requires closed
source userspace code, nor should you even want me to do that if you
rely on Linux.

So please, release the userspace code, as it's going to have to be
changed anyway as your current user/kernel api is broken/incorrect
as-is.  Why not just bundle it in the kernel tree like we have the usbip
code?  That way you know it all works properly, and better yet, it can
be tested and maintained properly over time.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v6 0/8] Add MA USB Host driver
  2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
                       ` (8 preceding siblings ...)
  2020-04-28 11:04     ` [PATCH v5 0/8] Add MA USB Host driver Greg KH
@ 2020-05-15 12:34     ` Vladimir Stankovic
  2020-05-15 12:34       ` [PATCH v6 1/8] usb: Add MA-USB Host kernel module Vladimir Stankovic
                         ` (8 more replies)
  9 siblings, 9 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:34 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Media Agnostic (MA) USB Host driver provides USB connectivity over an
available network, allowing host device to access remote USB devices
attached to one or more MA USB devices (accessible via network).

This driver has been developed to enable the host to communicate
with DisplayLink products supporting MA USB protocol (MA USB device,
in terms of MA USB Specification).

MA USB protocol used by MA USB Host driver has been implemented in
accordance with MA USB Specification Release 1.0b.

This driver depends on the functions provided by DisplayLink's
user-space driver.

v2:
- Fixed licensing info in headers
- Reorganized code to lower file count
- Patch has been split into 8 smaller patches

v3:
- Fixed nested spinlock usage
- Implemented IPv6 support

v4:
- No code changes
- Re-sent patch with different mail client config

v5:
- Updated kernel configuration with MA info
- Addressed build warnings for c6x architecture
- Fixed coccinelle warnings

v6:
- Changed module location to drivers/usb/host/mausb
- Switched from custom to dev_* logging
- Utilize misc device for the driver
- Removed versioning info from log entries

Vladimir Stankovic (8):
  usb: Add MA-USB Host kernel module
  usb: mausb_host: Add link layer implementation
  usb: mausb_host: HCD initialization
  usb: mausb_host: Implement initial hub handlers
  usb: mausb_host: Introduce PAL processing
  usb: mausb_host: Add logic for PAL-to-PAL communication
  usb: mausb_host: MA-USB PAL events processing
  usb: mausb_host: Process MA-USB data packets

 MAINTAINERS                                  |    7 +
 drivers/usb/Kconfig                          |    2 +
 drivers/usb/Makefile                         |    1 +
 drivers/usb/host/mausb/Kconfig               |   15 +
 drivers/usb/host/mausb/Makefile              |   15 +
 drivers/usb/host/mausb/hcd.c                 | 1780 +++++++++++++++
 drivers/usb/host/mausb/hcd.h                 |  154 ++
 drivers/usb/host/mausb/hpal.c                | 2094 ++++++++++++++++++
 drivers/usb/host/mausb/hpal.h                |  340 +++
 drivers/usb/host/mausb/hpal_data.c           |  713 ++++++
 drivers/usb/host/mausb/hpal_data.h           |   34 +
 drivers/usb/host/mausb/hpal_events.c         |  614 +++++
 drivers/usb/host/mausb/hpal_events.h         |   85 +
 drivers/usb/host/mausb/ip_link.c             |  367 +++
 drivers/usb/host/mausb/ip_link.h             |   88 +
 drivers/usb/host/mausb/ma_usb.h              |  869 ++++++++
 drivers/usb/host/mausb/mausb_address.h       |   26 +
 drivers/usb/host/mausb/mausb_core.c          |  191 ++
 drivers/usb/host/mausb/mausb_driver_status.h |   17 +
 drivers/usb/host/mausb/mausb_event.h         |  224 ++
 drivers/usb/host/mausb/utils.c               |  317 +++
 drivers/usb/host/mausb/utils.h               |   16 +
 22 files changed, 7969 insertions(+)
 create mode 100644 drivers/usb/host/mausb/Kconfig
 create mode 100644 drivers/usb/host/mausb/Makefile
 create mode 100644 drivers/usb/host/mausb/hcd.c
 create mode 100644 drivers/usb/host/mausb/hcd.h
 create mode 100644 drivers/usb/host/mausb/hpal.c
 create mode 100644 drivers/usb/host/mausb/hpal.h
 create mode 100644 drivers/usb/host/mausb/hpal_data.c
 create mode 100644 drivers/usb/host/mausb/hpal_data.h
 create mode 100644 drivers/usb/host/mausb/hpal_events.c
 create mode 100644 drivers/usb/host/mausb/hpal_events.h
 create mode 100644 drivers/usb/host/mausb/ip_link.c
 create mode 100644 drivers/usb/host/mausb/ip_link.h
 create mode 100644 drivers/usb/host/mausb/ma_usb.h
 create mode 100644 drivers/usb/host/mausb/mausb_address.h
 create mode 100644 drivers/usb/host/mausb/mausb_core.c
 create mode 100644 drivers/usb/host/mausb/mausb_driver_status.h
 create mode 100644 drivers/usb/host/mausb/mausb_event.h
 create mode 100644 drivers/usb/host/mausb/utils.c
 create mode 100644 drivers/usb/host/mausb/utils.h


base-commit: 24085f70a6e1b0cb647ec92623284641d8270637
-- 
2.17.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH v6 1/8] usb: Add MA-USB Host kernel module
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
@ 2020-05-15 12:34       ` Vladimir Stankovic
  2020-05-15 13:01         ` Greg KH
  2020-05-15 13:02         ` Greg KH
  2020-05-15 12:34       ` [PATCH v6 2/8] usb: mausb_host: Add link layer implementation Vladimir Stankovic
                         ` (7 subsequent siblings)
  8 siblings, 2 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:34 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Added utility macros, kernel device creation and cleanup, functions for
handling log formatting and a placeholder module for MA-USB Host device
driver.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 MAINTAINERS                         |  7 +++++
 drivers/usb/Kconfig                 |  2 ++
 drivers/usb/Makefile                |  1 +
 drivers/usb/host/mausb/Kconfig      | 15 +++++++++
 drivers/usb/host/mausb/Makefile     | 10 ++++++
 drivers/usb/host/mausb/mausb_core.c | 24 +++++++++++++++
 drivers/usb/host/mausb/utils.c      | 47 +++++++++++++++++++++++++++++
 drivers/usb/host/mausb/utils.h      | 15 +++++++++
 8 files changed, 121 insertions(+)
 create mode 100644 drivers/usb/host/mausb/Kconfig
 create mode 100644 drivers/usb/host/mausb/Makefile
 create mode 100644 drivers/usb/host/mausb/mausb_core.c
 create mode 100644 drivers/usb/host/mausb/utils.c
 create mode 100644 drivers/usb/host/mausb/utils.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 091ec22c1a23..9b7b79215f47 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10343,6 +10343,13 @@ W:	https://linuxtv.org
 T:	git git://linuxtv.org/media_tree.git
 F:	drivers/media/radio/radio-maxiradio*
 
+MEDIA AGNOSTIC (MA) USB HOST DRIVER
+M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
+L:	mausb-host-devel@displaylink.com
+W:	https://www.displaylink.com
+S:	Maintained
+F:	drivers/usb/host/mausb/*
+
 MCAN MMIO DEVICE DRIVER
 M:	Dan Murphy <dmurphy@ti.com>
 M:	Sriram Dash <sriram.dash@samsung.com>
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 275568abc670..35f5c3ac6656 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -99,6 +99,8 @@ source "drivers/usb/mon/Kconfig"
 
 source "drivers/usb/host/Kconfig"
 
+source "drivers/usb/host/mausb/Kconfig"
+
 source "drivers/usb/renesas_usbhs/Kconfig"
 
 source "drivers/usb/class/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 1c1c1d659394..2389b55723ab 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_IMX21_HCD)	+= host/
 obj-$(CONFIG_USB_FSL_USB2)	+= host/
 obj-$(CONFIG_USB_FOTG210_HCD)	+= host/
 obj-$(CONFIG_USB_MAX3421_HCD)	+= host/
+obj-$(CONFIG_USB_HOST_MAUSB)	+= host/mausb/
 
 obj-$(CONFIG_USB_C67X00_HCD)	+= c67x00/
 
diff --git a/drivers/usb/host/mausb/Kconfig b/drivers/usb/host/mausb/Kconfig
new file mode 100644
index 000000000000..ba72ef42f942
--- /dev/null
+++ b/drivers/usb/host/mausb/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Kernel configuration file for MA-USB Host driver.
+#
+# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+#
+
+config USB_HOST_MAUSB
+	tristate "Media Agnostic (MA) USB Host Driver"
+	depends on USB=y
+	help
+	  This is a Media Agnostic (MA) USB Host driver which enables host
+	  communication via MA USB protocol stack.
+
+	  If this driver is compiled as a module, it will be named mausb_host.
diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
new file mode 100644
index 000000000000..cafccac0edba
--- /dev/null
+++ b/drivers/usb/host/mausb/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for DisplayLink MA-USB Host driver.
+#
+# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+#
+
+obj-$(CONFIG_USB_HOST_MAUSB) += mausb_host.o
+mausb_host-y := mausb_core.o
+mausb_host-y += utils.o
diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
new file mode 100644
index 000000000000..44f76a1b74de
--- /dev/null
+++ b/drivers/usb/host/mausb/mausb_core.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include <linux/module.h>
+
+#include "utils.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("DisplayLink (UK) Ltd.");
+
+static int mausb_host_init(void)
+{
+	return mausb_host_dev_register();
+}
+
+static void mausb_host_exit(void)
+{
+	dev_info(mausb_host_dev.this_device, "Module unloading started...");
+	mausb_host_dev_deregister();
+}
+
+module_init(mausb_host_init);
+module_exit(mausb_host_exit);
diff --git a/drivers/usb/host/mausb/utils.c b/drivers/usb/host/mausb/utils.c
new file mode 100644
index 000000000000..1cfa2140311e
--- /dev/null
+++ b/drivers/usb/host/mausb/utils.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "utils.h"
+
+#include <linux/fs.h>
+#include <linux/slab.h>
+
+#define MAUSB_KERNEL_DEV_NAME "mausb_host"
+#define MAUSB_READ_DEVICE_TIMEOUT_MS 500
+
+struct miscdevice mausb_host_dev;
+
+static int mausb_host_dev_open(struct inode *inode, struct file *filp)
+{
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static int mausb_host_dev_release(struct inode *inode, struct file *filp)
+{
+	kfree(filp->private_data);
+	filp->private_data = NULL;
+
+	return 0;
+}
+
+static const struct file_operations mausb_host_dev_fops = {
+	.open	 = mausb_host_dev_open,
+	.release = mausb_host_dev_release,
+};
+
+int mausb_host_dev_register(void)
+{
+	mausb_host_dev.minor = MISC_DYNAMIC_MINOR;
+	mausb_host_dev.name = MAUSB_KERNEL_DEV_NAME;
+	mausb_host_dev.fops = &mausb_host_dev_fops;
+	mausb_host_dev.mode = 0;
+	return misc_register(&mausb_host_dev);
+}
+
+void mausb_host_dev_deregister(void)
+{
+	misc_deregister(&mausb_host_dev);
+}
diff --git a/drivers/usb/host/mausb/utils.h b/drivers/usb/host/mausb/utils.h
new file mode 100644
index 000000000000..e810e691c39d
--- /dev/null
+++ b/drivers/usb/host/mausb/utils.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_UTILS_H__
+#define __MAUSB_UTILS_H__
+
+#include <linux/miscdevice.h>
+
+extern struct miscdevice mausb_host_dev;
+
+int mausb_host_dev_register(void);
+void mausb_host_dev_deregister(void);
+
+#endif /* __MAUSB_UTILS_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 2/8] usb: mausb_host: Add link layer implementation
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
  2020-05-15 12:34       ` [PATCH v6 1/8] usb: Add MA-USB Host kernel module Vladimir Stankovic
@ 2020-05-15 12:34       ` Vladimir Stankovic
  2020-05-15 12:34       ` [PATCH v6 3/8] usb: mausb_host: HCD initialization Vladimir Stankovic
                         ` (6 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:34 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented link layer using kernel sockets. Link layer
manages network communication and provides interface for upper
layers of MA-USB stack.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/Makefile  |   1 +
 drivers/usb/host/mausb/ip_link.c | 367 +++++++++++++++++++++++++++++++
 drivers/usb/host/mausb/ip_link.h |  88 ++++++++
 3 files changed, 456 insertions(+)
 create mode 100644 drivers/usb/host/mausb/ip_link.c
 create mode 100644 drivers/usb/host/mausb/ip_link.h

diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
index cafccac0edba..acc8ad3f0eff 100644
--- a/drivers/usb/host/mausb/Makefile
+++ b/drivers/usb/host/mausb/Makefile
@@ -8,3 +8,4 @@
 obj-$(CONFIG_USB_HOST_MAUSB) += mausb_host.o
 mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
+mausb_host-y += ip_link.o
diff --git a/drivers/usb/host/mausb/ip_link.c b/drivers/usb/host/mausb/ip_link.c
new file mode 100644
index 000000000000..74bf6cce3c7a
--- /dev/null
+++ b/drivers/usb/host/mausb/ip_link.c
@@ -0,0 +1,367 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "ip_link.h"
+
+#include <net/tcp.h>
+
+#include "utils.h"
+
+static void __mausb_ip_connect(struct work_struct *work);
+static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx);
+static void __mausb_ip_recv_work(struct work_struct *work);
+static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx
+					     *recv_ctx);
+static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx
+					    *recv_ctx);
+
+int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx,
+		      struct net *net_ns,
+		      char ip_addr[INET6_ADDRSTRLEN],
+		      u16 port, void *context,
+		      void (*fn_callback)(void *ctx, enum mausb_channel channel,
+					  enum mausb_link_action act,
+					  int status, void *data),
+		      enum mausb_channel channel)
+{
+	struct mausb_ip_ctx *ctx;
+
+	*ip_ctx = kzalloc(sizeof(**ip_ctx), GFP_ATOMIC);
+	if (!*ip_ctx)
+		return -ENOMEM;
+
+	ctx = *ip_ctx;
+	ctx->client_socket = NULL;
+	__mausb_ip_recv_ctx_clear(&ctx->recv_ctx);
+
+	if (in4_pton(ip_addr, -1,
+		     (u8 *)&ctx->dev_addr_in.sa_in.sin_addr.s_addr, -1,
+		     NULL) == 1) {
+		ctx->dev_addr_in.sa_in.sin_family = AF_INET;
+		ctx->dev_addr_in.sa_in.sin_port = htons(port);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (in6_pton(ip_addr, -1,
+			    (u8 *)&ctx->dev_addr_in.sa_in6.sin6_addr.in6_u, -1,
+			    NULL) == 1) {
+		ctx->dev_addr_in.sa_in6.sin6_family = AF_INET6;
+		ctx->dev_addr_in.sa_in6.sin6_port = htons(port);
+#endif
+	} else {
+		dev_err(mausb_host_dev.this_device, "Invalid IP address received: address=%s",
+			ip_addr);
+		kfree(ctx);
+		return -EINVAL;
+	}
+
+	ctx->net_ns = net_ns;
+
+	if (channel == MAUSB_ISOCH_CHANNEL)
+		ctx->udp = true;
+
+	ctx->connect_workq = alloc_ordered_workqueue("connect_workq",
+						     WQ_MEM_RECLAIM);
+	if (!ctx->connect_workq) {
+		kfree(ctx);
+		return -ENOMEM;
+	}
+
+	ctx->recv_workq = alloc_ordered_workqueue("recv_workq", WQ_MEM_RECLAIM);
+	if (!ctx->recv_workq) {
+		destroy_workqueue(ctx->connect_workq);
+		kfree(ctx);
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&ctx->connect_work, __mausb_ip_connect);
+	INIT_WORK(&ctx->recv_work, __mausb_ip_recv_work);
+
+	ctx->channel	 = channel;
+	ctx->ctx	 = context;
+	ctx->fn_callback = fn_callback;
+
+	return 0;
+}
+
+void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx)
+{
+	if (!ip_ctx)
+		return;
+
+	if (ip_ctx->connect_workq) {
+		flush_workqueue(ip_ctx->connect_workq);
+		destroy_workqueue(ip_ctx->connect_workq);
+	}
+
+	if (ip_ctx->recv_workq) {
+		flush_workqueue(ip_ctx->recv_workq);
+		destroy_workqueue(ip_ctx->recv_workq);
+	}
+	if (ip_ctx->client_socket)
+		sock_release(ip_ctx->client_socket);
+	__mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx);
+
+	kfree(ip_ctx);
+}
+
+static void __mausb_ip_set_options(struct socket *sock, bool udp)
+{
+	u32 optval = 0;
+	unsigned int optlen = sizeof(optval);
+	int status = 0;
+	struct __kernel_sock_timeval timeo = {.tv_sec = 0, .tv_usec = 500000U };
+	struct __kernel_sock_timeval send_timeo = {.tv_sec = 1, .tv_usec = 0 };
+
+	if (!udp) {
+		optval = 1;
+		status = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+					   (char *)&optval, optlen);
+		if (status < 0)
+			dev_warn(mausb_host_dev.this_device, "Failed to set tcp no delay option: status=%d",
+				 status);
+	}
+
+	status = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_NEW,
+				   (char *)&timeo, sizeof(timeo));
+	if (status < 0)
+		dev_warn(mausb_host_dev.this_device, "Failed to set recv timeout option: status=%d",
+			 status);
+
+	status = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_NEW,
+				   (char *)&send_timeo, sizeof(send_timeo));
+	if (status < 0)
+		dev_warn(mausb_host_dev.this_device, "Failed to set snd timeout option: status=%d",
+			 status);
+
+	optval = MAUSB_LINK_BUFF_SIZE;
+	status  = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		dev_warn(mausb_host_dev.this_device, "Failed to set recv buffer size: status=%d",
+			 status);
+
+	optval = MAUSB_LINK_BUFF_SIZE;
+	status  = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		dev_warn(mausb_host_dev.this_device, "Failed to set send buffer size: status=%d",
+			 status);
+
+	optval = MAUSB_LINK_TOS_LEVEL_EF;
+	status  = kernel_setsockopt(sock, IPPROTO_IP, IP_TOS,
+				    (char *)&optval, optlen);
+	if (status < 0)
+		dev_warn(mausb_host_dev.this_device, "Failed to set QOS: status=%d",
+			 status);
+}
+
+static void __mausb_ip_connect(struct work_struct *work)
+{
+	int status = 0;
+	struct sockaddr *sa;
+	int sa_size;
+	struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx,
+						   connect_work);
+	unsigned short int family = ip_ctx->dev_addr_in.sa_in.sin_family;
+
+	if (!ip_ctx->udp) {
+		status = sock_create_kern(ip_ctx->net_ns, family, SOCK_STREAM,
+					  IPPROTO_TCP, &ip_ctx->client_socket);
+		if (status < 0) {
+			dev_err(mausb_host_dev.this_device, "Failed to create socket: status=%d",
+				status);
+			goto callback;
+		}
+	} else {
+		status = sock_create_kern(ip_ctx->net_ns, family, SOCK_DGRAM,
+					  IPPROTO_UDP, &ip_ctx->client_socket);
+		if (status < 0) {
+			dev_err(mausb_host_dev.this_device, "Failed to create socket: status=%d",
+				status);
+			goto callback;
+		}
+	}
+
+	__mausb_ip_set_options((struct socket *)ip_ctx->client_socket,
+			       ip_ctx->udp);
+
+	if (family == AF_INET) {
+		sa = (struct sockaddr *)&ip_ctx->dev_addr_in.sa_in;
+		sa_size = sizeof(ip_ctx->dev_addr_in.sa_in);
+		dev_info(mausb_host_dev.this_device, "Connecting to %pI4:%d, status=%d",
+			 &ip_ctx->dev_addr_in.sa_in.sin_addr,
+			 htons(ip_ctx->dev_addr_in.sa_in.sin_port), status);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if (family == AF_INET6) {
+		sa = (struct sockaddr *)&ip_ctx->dev_addr_in.sa_in6;
+		sa_size = sizeof(ip_ctx->dev_addr_in.sa_in6);
+		dev_info(mausb_host_dev.this_device, "Connecting to %pI6c:%d, status=%d",
+			 &ip_ctx->dev_addr_in.sa_in6.sin6_addr,
+			 htons(ip_ctx->dev_addr_in.sa_in6.sin6_port), status);
+#endif
+	} else {
+		dev_err(mausb_host_dev.this_device, "Wrong network family provided");
+		status = -EINVAL;
+		goto callback;
+	}
+
+	status = kernel_connect(ip_ctx->client_socket, sa, sa_size, O_RDWR);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Failed to connect to host, status=%d",
+			status);
+		goto clear_socket;
+	}
+
+	queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work);
+
+	goto callback;
+
+clear_socket:
+	sock_release(ip_ctx->client_socket);
+	ip_ctx->client_socket = NULL;
+callback:
+	ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel, MAUSB_LINK_CONNECT,
+			    status, NULL);
+}
+
+void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx)
+{
+	queue_work(ip_ctx->connect_workq, &ip_ctx->connect_work);
+}
+
+int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx)
+{
+	if (ip_ctx && ip_ctx->client_socket)
+		return kernel_sock_shutdown(ip_ctx->client_socket, SHUT_RDWR);
+	return 0;
+}
+
+int mausb_ip_send(struct mausb_ip_ctx *ip_ctx,
+		  struct mausb_kvec_data_wrapper *wrapper)
+{
+	struct msghdr msghd;
+
+	if (!ip_ctx) {
+		dev_alert(mausb_host_dev.this_device, "Socket ctx is NULL!");
+		return -EINVAL;
+	}
+
+	memset(&msghd, 0, sizeof(msghd));
+	msghd.msg_flags = MSG_WAITALL;
+
+	return kernel_sendmsg(ip_ctx->client_socket, &msghd, wrapper->kvec,
+			      wrapper->kvec_num, wrapper->length);
+}
+
+static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx *recv_ctx)
+{
+	recv_ctx->buffer   = NULL;
+	recv_ctx->left	   = 0;
+	recv_ctx->received = 0;
+}
+
+static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx *recv_ctx)
+{
+	kfree(recv_ctx->buffer);
+	__mausb_ip_recv_ctx_clear(recv_ctx);
+}
+
+static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx)
+{
+	struct msghdr msghd;
+	struct kvec vec;
+	int  status;
+	bool peek = true;
+	unsigned int optval = 1;
+	struct socket *client_socket = (struct socket *)ip_ctx->client_socket;
+
+	/* receive with timeout of 0.5s */
+	while (true) {
+		memset(&msghd, 0, sizeof(msghd));
+		if (peek) {
+			vec.iov_base = ip_ctx->recv_ctx.common_hdr;
+			vec.iov_len  = sizeof(ip_ctx->recv_ctx.common_hdr);
+			msghd.msg_flags = MSG_PEEK;
+		} else {
+			vec.iov_base =
+			    ip_ctx->recv_ctx.buffer +
+			    ip_ctx->recv_ctx.received;
+			vec.iov_len = ip_ctx->recv_ctx.left;
+			msghd.msg_flags = MSG_WAITALL;
+		}
+
+		if (!ip_ctx->udp) {
+			status = kernel_setsockopt(client_socket, IPPROTO_TCP,
+						   TCP_QUICKACK,
+						   (char *)&optval,
+						   sizeof(optval));
+			if (status != 0) {
+				dev_warn(mausb_host_dev.this_device, "Setting TCP_QUICKACK failed, status=%d",
+					 status);
+			}
+		}
+
+		status = kernel_recvmsg(client_socket, &msghd, &vec, 1,
+					vec.iov_len, (int)msghd.msg_flags);
+		if (status == -EAGAIN) {
+			return -EAGAIN;
+		} else if (status <= 0) {
+			dev_warn(mausb_host_dev.this_device, "kernel_recvmsg, status=%d",
+				 status);
+
+			__mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx);
+			ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel,
+					    MAUSB_LINK_RECV, status, NULL);
+			return status;
+		}
+
+		dev_vdbg(mausb_host_dev.this_device, "kernel_recvmsg, status=%d",
+			 status);
+
+		if (peek) {
+			if ((unsigned int)status <
+					sizeof(ip_ctx->recv_ctx.common_hdr))
+				return -EAGAIN;
+			/* length field of mausb_common_hdr */
+			ip_ctx->recv_ctx.left =
+			    *(u16 *)(&ip_ctx->recv_ctx.common_hdr[2]);
+			ip_ctx->recv_ctx.received = 0;
+			ip_ctx->recv_ctx.buffer	  =
+			    kzalloc(ip_ctx->recv_ctx.left, GFP_KERNEL);
+			peek = false;
+			if (!ip_ctx->recv_ctx.buffer) {
+				ip_ctx->fn_callback(ip_ctx->ctx,
+						    ip_ctx->channel,
+						    MAUSB_LINK_RECV,
+						    -ENOMEM, NULL);
+				return -ENOMEM;
+			}
+		} else {
+			if (status < ip_ctx->recv_ctx.left) {
+				ip_ctx->recv_ctx.left -= (u16)status;
+				ip_ctx->recv_ctx.received += (u16)status;
+			} else {
+				ip_ctx->fn_callback(ip_ctx->ctx,
+						    ip_ctx->channel,
+						    MAUSB_LINK_RECV, status,
+						    ip_ctx->recv_ctx.buffer);
+				__mausb_ip_recv_ctx_clear(&ip_ctx->recv_ctx);
+				peek = true;
+			}
+		}
+	}
+
+	return status;
+}
+
+static void __mausb_ip_recv_work(struct work_struct *work)
+{
+	struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx,
+						   recv_work);
+	int status = __mausb_ip_recv(ip_ctx);
+
+	if (status <= 0 && status != -EAGAIN)
+		return;
+
+	queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work);
+}
diff --git a/drivers/usb/host/mausb/ip_link.h b/drivers/usb/host/mausb/ip_link.h
new file mode 100644
index 000000000000..faa5a9acabbd
--- /dev/null
+++ b/drivers/usb/host/mausb/ip_link.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_IP_LINK_H__
+#define __MAUSB_IP_LINK_H__
+
+#include <linux/inet.h>
+#include <linux/list.h>
+#include <linux/workqueue.h>
+
+#define MAUSB_LINK_BUFF_SIZE	16777216
+#define MAUSB_LINK_TOS_LEVEL_EF 0xB8
+
+extern struct miscdevice mausb_host_dev;
+
+enum mausb_link_action {
+	MAUSB_LINK_CONNECT	= 0,
+	MAUSB_LINK_DISCONNECT	= 1,
+	MAUSB_LINK_RECV		= 2,
+	MAUSB_LINK_SEND		= 3
+};
+
+enum mausb_channel {
+	MAUSB_CTRL_CHANNEL  = 0,
+	MAUSB_ISOCH_CHANNEL = 1,
+	MAUSB_BULK_CHANNEL  = 2,
+	MAUSB_INTR_CHANNEL  = 3,
+	MAUSB_MGMT_CHANNEL  = 4
+};
+
+struct mausb_kvec_data_wrapper {
+	struct kvec *kvec;
+	u32    kvec_num;
+	u32    length;
+};
+
+struct mausb_ip_recv_ctx {
+	u16  left;
+	u16  received;
+	char *buffer;
+	char common_hdr[12] __aligned(4);
+};
+
+struct mausb_ip_ctx {
+	struct socket		*client_socket;
+	union {
+		struct sockaddr_in sa_in;
+#if IS_ENABLED(CONFIG_IPV6)
+		struct sockaddr_in6 sa_in6;
+#endif
+	} dev_addr_in;
+	struct net		*net_ns;
+	bool			udp;
+
+	/* Queues to schedule rx work */
+	struct workqueue_struct	*recv_workq;
+	struct workqueue_struct	*connect_workq;
+	struct work_struct	recv_work;
+	struct work_struct	connect_work;
+
+	struct mausb_ip_recv_ctx recv_ctx; /* recv buffer */
+
+	enum mausb_channel channel;
+	void *ctx;
+	/* callback should store task into hpal queue */
+	void (*fn_callback)(void *ctx, enum mausb_channel channel,
+			    enum mausb_link_action act, int status, void *data);
+};
+
+int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx,
+		      struct net *net_ns,
+		      char ip_addr[INET6_ADDRSTRLEN],
+		      u16 port,
+		      void *ctx,
+		      void (*ctx_callback)(void *ctx,
+					   enum mausb_channel channel,
+					   enum mausb_link_action act,
+					   int status, void *data),
+		      enum mausb_channel channel);
+int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx);
+int mausb_ip_send(struct mausb_ip_ctx *ip_ctx,
+		  struct mausb_kvec_data_wrapper *wrapper);
+
+void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx);
+void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx);
+
+#endif /* __MAUSB_IP_LINK_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 3/8] usb: mausb_host: HCD initialization
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
  2020-05-15 12:34       ` [PATCH v6 1/8] usb: Add MA-USB Host kernel module Vladimir Stankovic
  2020-05-15 12:34       ` [PATCH v6 2/8] usb: mausb_host: Add link layer implementation Vladimir Stankovic
@ 2020-05-15 12:34       ` Vladimir Stankovic
  2020-05-15 13:03         ` Greg KH
  2020-05-15 13:07         ` Greg KH
  2020-05-15 12:34       ` [PATCH v6 4/8] usb: mausb_host: Implement initial hub handlers Vladimir Stankovic
                         ` (5 subsequent siblings)
  8 siblings, 2 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:34 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented HCD initialization/deinitialization functionality.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/Makefile     |   1 +
 drivers/usb/host/mausb/hcd.c        | 120 ++++++++++++++++++++++++++++
 drivers/usb/host/mausb/hcd.h        |  65 +++++++++++++++
 drivers/usb/host/mausb/mausb_core.c |  18 ++++-
 4 files changed, 203 insertions(+), 1 deletion(-)
 create mode 100644 drivers/usb/host/mausb/hcd.c
 create mode 100644 drivers/usb/host/mausb/hcd.h

diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
index acc8ad3f0eff..4d655cb7fab4 100644
--- a/drivers/usb/host/mausb/Makefile
+++ b/drivers/usb/host/mausb/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_USB_HOST_MAUSB) += mausb_host.o
 mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
 mausb_host-y += ip_link.o
+mausb_host-y += hcd.o
diff --git a/drivers/usb/host/mausb/hcd.c b/drivers/usb/host/mausb/hcd.c
new file mode 100644
index 000000000000..3a6ef9313dcb
--- /dev/null
+++ b/drivers/usb/host/mausb/hcd.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hcd.h"
+
+#include "utils.h"
+
+static int mausb_bus_match(struct device *dev, struct device_driver *drv);
+
+static const struct file_operations mausb_fops;
+
+static unsigned int major;
+static unsigned int minor = 1;
+static dev_t devt;
+static struct device *device;
+
+struct mausb_hcd	*mhcd;
+static struct class	*mausb_class;
+static struct bus_type	mausb_bus_type = {
+	.name	= DEVICE_NAME,
+	.match	= mausb_bus_match,
+};
+
+static struct device_driver mausb_driver = {
+	.name	= DEVICE_NAME,
+	.bus	= &mausb_bus_type,
+	.owner	= THIS_MODULE,
+};
+
+static void mausb_remove(void)
+{
+	struct usb_hcd *hcd, *shared_hcd;
+
+	hcd	   = mhcd->hcd_hs_ctx->hcd;
+	shared_hcd = mhcd->hcd_ss_ctx->hcd;
+
+	if (shared_hcd) {
+		usb_remove_hcd(shared_hcd);
+		usb_put_hcd(shared_hcd);
+		mhcd->hcd_ss_ctx = NULL;
+	}
+
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+	mhcd->hcd_hs_ctx = NULL;
+}
+
+static int mausb_bus_match(struct device *dev, struct device_driver *drv)
+{
+	if (strncmp(dev->bus->name, drv->name, strlen(drv->name)))
+		return 0;
+	else
+		return 1;
+}
+
+int mausb_init_hcd(void)
+{
+	int retval = register_chrdev(0, DEVICE_NAME, &mausb_fops);
+
+	if (retval < 0)
+		return retval;
+
+	major = (unsigned int)retval;
+	retval = bus_register(&mausb_bus_type);
+	if (retval)
+		goto bus_register_error;
+
+	mausb_class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(mausb_class)) {
+		retval = (int)PTR_ERR(mausb_class);
+		goto class_error;
+	}
+
+	retval = driver_register(&mausb_driver);
+	if (retval)
+		goto driver_register_error;
+
+	mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC);
+	if (!mhcd) {
+		retval = -ENOMEM;
+		goto mausb_hcd_alloc_failed;
+	}
+
+	devt = MKDEV(major, minor);
+	device = device_create(mausb_class, NULL, devt, mhcd, DEVICE_NAME);
+	if (IS_ERR(device)) {
+		retval = (int)IS_ERR(device);
+		goto device_create_error;
+	}
+
+	device->driver = &mausb_driver;
+
+	return retval;
+device_create_error:
+	kfree(mhcd);
+	mhcd = NULL;
+mausb_hcd_alloc_failed:
+	driver_unregister(&mausb_driver);
+driver_register_error:
+	class_destroy(mausb_class);
+class_error:
+	bus_unregister(&mausb_bus_type);
+bus_register_error:
+	unregister_chrdev(major, DEVICE_NAME);
+
+	return retval;
+}
+
+void mausb_deinit_hcd(void)
+{
+	if (mhcd) {
+		mausb_remove();
+		device_destroy(mausb_class, devt);
+		driver_unregister(&mausb_driver);
+		class_destroy(mausb_class);
+		bus_unregister(&mausb_bus_type);
+		unregister_chrdev(major, DEVICE_NAME);
+	}
+}
diff --git a/drivers/usb/host/mausb/hcd.h b/drivers/usb/host/mausb/hcd.h
new file mode 100644
index 000000000000..e2374af67663
--- /dev/null
+++ b/drivers/usb/host/mausb/hcd.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HCD_H__
+#define __MAUSB_HCD_H__
+
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#define DEVICE_NAME "mausb_host_hcd"
+#define CLASS_NAME "mausb"
+
+#define NUMBER_OF_PORTS		15
+
+#define MAX_USB_DEVICE_DEPTH	6
+
+#define RESPONSE_TIMEOUT	5000
+
+#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
+#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
+
+enum mausb_device_type {
+	USBDEVICE = 0,
+	USB20HUB  = 1,
+	USB30HUB  = 2
+};
+
+enum mausb_device_speed {
+	LOW_SPEED	 = 0,
+	FULL_SPEED	 = 1,
+	HIGH_SPEED	 = 2,
+	SUPER_SPEED	 = 3,
+	SUPER_SPEED_PLUS = 4
+};
+
+struct mausb_hcd {
+	spinlock_t	lock;	/* Protect HCD during URB processing */
+	struct device	*pdev;
+	u16		connected_ports;
+
+	struct rb_root	mausb_urbs;
+	struct hub_ctx	*hcd_ss_ctx;
+	struct hub_ctx	*hcd_hs_ctx;
+	struct notifier_block power_state_listener;
+};
+
+struct mausb_dev {
+	u32		port_status;
+	struct rb_root	usb_devices;
+	u8		dev_speed;
+	void		*ma_dev;
+};
+
+struct hub_ctx {
+	struct mausb_hcd *mhcd;
+	struct usb_hcd	 *hcd;
+	struct mausb_dev ma_devs[NUMBER_OF_PORTS];
+};
+
+int mausb_init_hcd(void);
+void mausb_deinit_hcd(void);
+
+#endif /* __MAUSB_HCD_H__ */
diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
index 44f76a1b74de..485f241d2b4c 100644
--- a/drivers/usb/host/mausb/mausb_core.c
+++ b/drivers/usb/host/mausb/mausb_core.c
@@ -4,6 +4,7 @@
  */
 #include <linux/module.h>
 
+#include "hcd.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
@@ -11,12 +12,27 @@ MODULE_AUTHOR("DisplayLink (UK) Ltd.");
 
 static int mausb_host_init(void)
 {
-	return mausb_host_dev_register();
+	int status = mausb_host_dev_register();
+
+	if (status < 0)
+		goto exit;
+
+	status = mausb_init_hcd();
+	if (status < 0)
+		goto cleanup_dev;
+
+	return 0;
+
+cleanup_dev:
+	mausb_host_dev_deregister();
+exit:
+	return status;
 }
 
 static void mausb_host_exit(void)
 {
 	dev_info(mausb_host_dev.this_device, "Module unloading started...");
+	mausb_deinit_hcd();
 	mausb_host_dev_deregister();
 }
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 4/8] usb: mausb_host: Implement initial hub handlers
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
                         ` (2 preceding siblings ...)
  2020-05-15 12:34       ` [PATCH v6 3/8] usb: mausb_host: HCD initialization Vladimir Stankovic
@ 2020-05-15 12:34       ` Vladimir Stankovic
  2020-05-15 12:34       ` [PATCH v6 5/8] usb: mausb_host: Introduce PAL processing Vladimir Stankovic
                         ` (4 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:34 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented handlers for subset of HCD events.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/hcd.c | 925 +++++++++++++++++++++++++++++++++++
 drivers/usb/host/mausb/hcd.h |  86 +++-
 2 files changed, 1008 insertions(+), 3 deletions(-)

diff --git a/drivers/usb/host/mausb/hcd.c b/drivers/usb/host/mausb/hcd.c
index 3a6ef9313dcb..41b88b6f97b1 100644
--- a/drivers/usb/host/mausb/hcd.c
+++ b/drivers/usb/host/mausb/hcd.c
@@ -6,6 +6,7 @@
 
 #include "utils.h"
 
+static int mausb_bus_probe(struct device *dev);
 static int mausb_bus_match(struct device *dev, struct device_driver *drv);
 
 static const struct file_operations mausb_fops;
@@ -20,6 +21,7 @@ static struct class	*mausb_class;
 static struct bus_type	mausb_bus_type = {
 	.name	= DEVICE_NAME,
 	.match	= mausb_bus_match,
+	.probe	= mausb_bus_probe,
 };
 
 static struct device_driver mausb_driver = {
@@ -46,6 +48,11 @@ static void mausb_remove(void)
 	mhcd->hcd_hs_ctx = NULL;
 }
 
+static int mausb_bus_probe(struct device *dev)
+{
+	return mausb_probe(dev);
+}
+
 static int mausb_bus_match(struct device *dev, struct device_driver *drv)
 {
 	if (strncmp(dev->bus->name, drv->name, strlen(drv->name)))
@@ -91,7 +98,13 @@ int mausb_init_hcd(void)
 
 	device->driver = &mausb_driver;
 
+	retval = mausb_probe(device);
+	if (retval)
+		goto mausb_probe_failed;
+
 	return retval;
+mausb_probe_failed:
+	device_destroy(mausb_class, devt);
 device_create_error:
 	kfree(mhcd);
 	mhcd = NULL;
@@ -118,3 +131,915 @@ void mausb_deinit_hcd(void)
 		unregister_chrdev(major, DEVICE_NAME);
 	}
 }
+
+static const char driver_name[] = "MA-USB host controller";
+
+static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length);
+static void mausb_set_port_feature(struct usb_hcd *hcd, u16 type_req,
+				   u16 value, u16 index, char *buff,
+				   u16 length);
+static void mausb_get_port_status(struct usb_hcd *hcd, u16 type_req,
+				  u16 value, u16 index, char *buff,
+				  u16 length);
+static void mausb_clear_port_feature(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length);
+static void mausb_get_hub_status(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length);
+static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			      struct usb_host_endpoint *endpoint);
+static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			       struct usb_host_endpoint *endpoint);
+static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev);
+static void mausb_endpoint_reset(struct usb_hcd *hcd,
+				 struct usb_host_endpoint *endpoint);
+static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_hcd_bus_resume(struct usb_hcd *hcd);
+static int mausb_hcd_bus_suspend(struct usb_hcd *hcd);
+static int mausb_hcd_get_frame_number(struct usb_hcd *hcd);
+static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length);
+static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff);
+static int mausb_hcd_reset(struct usb_hcd *hcd);
+static int mausb_hcd_start(struct usb_hcd *hcd);
+static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
+				   struct usb_tt *tt, gfp_t mem_flags);
+static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
+static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
+
+static const struct hc_driver mausb_hc_driver = {
+	.description  =  driver_name,
+	.product_desc = driver_name,
+	.flags	      = HCD_USB3 | HCD_SHARED,
+
+	.hcd_priv_size = sizeof(struct hub_ctx),
+
+	.reset = mausb_hcd_reset,
+	.start = mausb_hcd_start,
+
+	.get_frame_number = mausb_hcd_get_frame_number,
+
+	.hub_status_data   = mausb_hcd_hub_status,
+	.hub_control	   = mausb_hcd_hub_control,
+	.update_hub_device = mausb_hub_update_device,
+	.bus_suspend	   = mausb_hcd_bus_suspend,
+	.bus_resume	   = mausb_hcd_bus_resume,
+
+	.alloc_dev	= mausb_alloc_dev,
+	.free_dev	= mausb_free_dev,
+	.enable_device	= mausb_enable_device,
+	.update_device	= mausb_update_device,
+	.reset_device	= mausb_reset_device,
+
+	.add_endpoint	  = mausb_add_endpoint,
+	.drop_endpoint	  = mausb_drop_endpoint,
+	.address_device   = mausb_address_device,
+	.endpoint_reset	  = mausb_endpoint_reset,
+};
+
+static struct {
+	struct usb_bos_descriptor    bos;
+	struct usb_ss_cap_descriptor ss_cap;
+} usb3_bos_desc = {
+	.bos = {
+		.bLength	 = USB_DT_BOS_SIZE,
+		.bDescriptorType = USB_DT_BOS,
+		.wTotalLength	 = cpu_to_le16(sizeof(usb3_bos_desc)),
+		.bNumDeviceCaps	 = 1
+	},
+	.ss_cap = {
+		.bLength		= USB_DT_USB_SS_CAP_SIZE,
+		.bDescriptorType	= USB_DT_DEVICE_CAPABILITY,
+		.bDevCapabilityType	= USB_SS_CAP_TYPE,
+		.wSpeedSupported	= cpu_to_le16(USB_5GBPS_OPERATION),
+		.bFunctionalitySupport	= ilog2(USB_5GBPS_OPERATION)
+	}
+};
+
+static int get_root_hub_port_number(struct usb_device *dev, u8 *port_number)
+{
+	struct usb_device *first_hub_device = dev;
+
+	if (!dev->parent) {
+		(*port_number) = 0;
+		return -EINVAL;
+	}
+
+	while (first_hub_device->parent->parent)
+		first_hub_device = first_hub_device->parent;
+
+	(*port_number) = first_hub_device->portnum - 1;
+
+	return 0;
+}
+
+static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
+							*mdevs, void *dev_addr)
+{
+	struct rb_node *node = mdevs->usb_devices.rb_node;
+
+	while (node) {
+		struct mausb_usb_device_ctx *usb_device =
+		    rb_entry(node, struct mausb_usb_device_ctx, rb_node);
+
+		if (dev_addr < usb_device->dev_addr)
+			node = usb_device->rb_node.rb_left;
+		else if (dev_addr > usb_device->dev_addr)
+			node = usb_device->rb_node.rb_right;
+		else
+			return usb_device;
+	}
+	return NULL;
+}
+
+static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
+{
+	return 0;
+}
+
+static int mausb_hcd_reset(struct usb_hcd *hcd)
+{
+	if (usb_hcd_is_primary_hcd(hcd)) {
+		hcd->speed = HCD_USB2;
+		hcd->self.root_hub->speed = USB_SPEED_HIGH;
+	} else {
+		hcd->speed = HCD_USB3;
+		hcd->self.root_hub->speed = USB_SPEED_SUPER;
+	}
+	hcd->self.no_sg_constraint = 1;
+	hcd->self.sg_tablesize = UINT_MAX;
+
+	return 0;
+}
+
+static int mausb_hcd_start(struct usb_hcd *hcd)
+{
+	hcd->power_budget = 0;
+	hcd->uses_new_polling = 1;
+	return 0;
+}
+
+static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff)
+{
+	int retval;
+	int changed;
+	int i;
+	struct hub_ctx *hub;
+	unsigned long flags = 0;
+
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	retval  = DIV_ROUND_UP(NUMBER_OF_PORTS + 1, 8);
+	changed = 0;
+
+	memset(buff, 0, (unsigned int)retval);
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		dev_info(mausb_host_dev.this_device, "hcd not accessible, hcd speed=%d",
+			 hcd->speed);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return 0;
+	}
+
+	for (i = 0; i < NUMBER_OF_PORTS; ++i) {
+		if ((hub->ma_devs[i].port_status & PORT_C_MASK)) {
+			buff[(i + 1) / 8] |= 1 << (i + 1) % 8;
+			changed = 1;
+		}
+	}
+
+	dev_info(mausb_host_dev.this_device, "Usb %d.0 : changed=%d, retval=%d",
+		 (hcd->speed == HCD_USB2) ? 2 : 3, changed, retval);
+
+	if (hcd->state == HC_STATE_SUSPENDED && changed == 1) {
+		dev_info(mausb_host_dev.this_device, "hcd state is suspended");
+		usb_hcd_resume_root_hub(hcd);
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return changed ? retval : 0;
+}
+
+static int mausb_hcd_bus_resume(struct usb_hcd *hcd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		dev_info(mausb_host_dev.this_device, "hcd not accessible, hcd speed=%d",
+			 hcd->speed);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return -ESHUTDOWN;
+	}
+	hcd->state = HC_STATE_RUNNING;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static int mausb_hcd_bus_suspend(struct usb_hcd *hcd)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	hcd->state = HC_STATE_SUSPENDED;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length)
+{
+	int retval = 0;
+	struct hub_ctx	 *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_hcd *hub_mhcd = hub->mhcd;
+	unsigned long	 flags;
+	bool invalid_rhport = false;
+
+	index = ((__u8)(index & 0x00ff));
+	if (index < 1 || index > NUMBER_OF_PORTS)
+		invalid_rhport = true;
+
+	dev_vdbg(mausb_host_dev.this_device, "TypeReq=%d", type_req);
+
+	spin_lock_irqsave(&hub_mhcd->lock, flags);
+
+	if (!HCD_HW_ACCESSIBLE(hcd)) {
+		dev_err(mausb_host_dev.this_device, "hcd not accessible, hcd speed=%d",
+			hcd->speed);
+		spin_unlock_irqrestore(&hub_mhcd->lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	switch (type_req) {
+	case ClearHubFeature:
+		break;
+	case ClearPortFeature:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_clear_port_feature(hcd, type_req, value, index, buff,
+					 length);
+		break;
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		memcpy(buff, &usb3_bos_desc, sizeof(usb3_bos_desc));
+		retval = sizeof(usb3_bos_desc);
+		break;
+	case GetHubDescriptor:
+		mausb_get_hub_descriptor(hcd, type_req, value, index, buff,
+					 length);
+		break;
+	case GetHubStatus:
+		mausb_get_hub_status(hcd, type_req, value, index, buff,
+				     length);
+		break;
+	case GetPortStatus:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_get_port_status(hcd, type_req, value, index, buff,
+				      length);
+		break;
+	case SetHubFeature:
+		retval = -EPIPE;
+		break;
+	case SetPortFeature:
+		if (invalid_rhport)
+			goto invalid_port;
+
+		mausb_set_port_feature(hcd, type_req, value, index, buff,
+				       length);
+		break;
+	default:
+		retval = -EPIPE;
+	}
+
+invalid_port:
+	spin_unlock_irqrestore(&hub_mhcd->lock, flags);
+	return retval;
+}
+
+int mausb_probe(struct device *dev)
+{
+	struct mausb_hcd *mausb_hcd;
+	struct usb_hcd	 *hcd_ss;
+	struct usb_hcd	 *hcd_hs;
+	int ret;
+
+	mausb_hcd = dev_get_drvdata(dev);
+	spin_lock_init(&mausb_hcd->lock);
+
+	hcd_hs = usb_create_hcd(&mausb_hc_driver, dev, dev_name(dev));
+	if (!hcd_hs)
+		return -ENOMEM;
+
+	hcd_hs->has_tt = 1;
+	mausb_hcd->hcd_hs_ctx	    = (struct hub_ctx *)hcd_hs->hcd_priv;
+	mausb_hcd->hcd_hs_ctx->mhcd = mausb_hcd;
+	mausb_hcd->hcd_hs_ctx->hcd  = hcd_hs;
+	memset(mausb_hcd->hcd_hs_ctx->ma_devs, 0,
+	       sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
+
+	ret = usb_add_hcd(hcd_hs, 0, 0);
+	if (ret) {
+		dev_err(dev, "usb_add_hcd failed");
+		goto put_hcd_hs;
+	}
+
+	hcd_ss = usb_create_shared_hcd(&mausb_hc_driver, dev, dev_name(dev),
+				       hcd_hs);
+	if (!hcd_ss) {
+		ret = -ENOMEM;
+		goto remove_hcd_hs;
+	}
+	mausb_hcd->hcd_ss_ctx	    = (struct hub_ctx *)hcd_ss->hcd_priv;
+	mausb_hcd->hcd_ss_ctx->mhcd = mausb_hcd;
+	mausb_hcd->hcd_ss_ctx->hcd  = hcd_ss;
+
+	memset(mausb_hcd->hcd_ss_ctx->ma_devs, 0,
+	       sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
+
+	ret = usb_add_hcd(hcd_ss, 0, 0);
+	if (ret) {
+		dev_err(dev, "usb_add_hcd failed");
+		goto put_hcd_ss;
+	}
+
+	return ret;
+
+put_hcd_ss:
+	usb_put_hcd(hcd_ss);
+remove_hcd_hs:
+	usb_remove_hcd(hcd_hs);
+put_hcd_hs:
+	usb_put_hcd(hcd_hs);
+	mausb_hcd->hcd_hs_ctx = NULL;
+	mausb_hcd->hcd_ss_ctx = NULL;
+	return ret;
+}
+
+static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length)
+{
+	u8 width;
+	struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buff;
+
+	memset(desc, 0, sizeof(struct usb_hub_descriptor));
+
+	if (hcd->speed == HCD_USB3) {
+		desc->bDescriptorType	   = USB_DT_SS_HUB;
+		desc->bDescLength	   = 12;
+		desc->wHubCharacteristics  =
+		    cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+		desc->bNbrPorts		   = NUMBER_OF_PORTS;
+		desc->u.ss.bHubHdrDecLat   = 0x04;
+		desc->u.ss.DeviceRemovable = cpu_to_le16(0xffff);
+	} else {
+		desc->bDescriptorType	  = USB_DT_HUB;
+		desc->wHubCharacteristics =
+		    cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM);
+		desc->bNbrPorts		  = NUMBER_OF_PORTS;
+		width			  = (u8)(desc->bNbrPorts / 8 + 1);
+		desc->bDescLength	  = USB_DT_HUB_NONVAR_SIZE + 2 * width;
+
+		memset(&desc->u.hs.DeviceRemovable[0], 0, width);
+		memset(&desc->u.hs.DeviceRemovable[width], 0xff, width);
+	}
+}
+
+static void mausb_set_port_feature(struct usb_hcd *hcd, u16 type_req,
+				   u16 value, u16 index, char *buff,
+				   u16 length)
+{
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	switch (value) {
+	case USB_PORT_FEAT_LINK_STATE:
+		dev_vdbg(mausb_host_dev.this_device, "USB_PORT_FEAT_LINK_STATE");
+		if (hcd->speed == HCD_USB3) {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		} else {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		}
+		break;
+	case USB_PORT_FEAT_U1_TIMEOUT:
+	case USB_PORT_FEAT_U2_TIMEOUT:
+		break;
+	case USB_PORT_FEAT_SUSPEND:
+		break;
+	case USB_PORT_FEAT_POWER:
+		dev_vdbg(mausb_host_dev.this_device, "USB_PORT_FEAT_POWER");
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status |=
+			    USB_SS_PORT_STAT_POWER;
+		} else {
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_POWER;
+		}
+		break;
+	case USB_PORT_FEAT_BH_PORT_RESET:
+		dev_dbg(mausb_host_dev.this_device, "USB_PORT_FEAT_BH_PORT_RESET");
+		/* fall through */
+	case USB_PORT_FEAT_RESET:
+		dev_vdbg(mausb_host_dev.this_device, "USB_PORT_FEAT_RESET hcd->speed=%d",
+			 hcd->speed);
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status = 0;
+			hub->ma_devs[index - 1].port_status =
+			    (USB_SS_PORT_STAT_POWER |
+			     USB_PORT_STAT_CONNECTION | USB_PORT_STAT_RESET);
+		} else if (hub->ma_devs[index - 1].port_status
+			   & USB_PORT_STAT_ENABLE) {
+			hub->ma_devs[index - 1].port_status &=
+			    ~(USB_PORT_STAT_ENABLE |
+			      USB_PORT_STAT_LOW_SPEED |
+			      USB_PORT_STAT_HIGH_SPEED);
+		}
+		/* fall through */
+	default:
+		dev_vdbg(mausb_host_dev.this_device, "Default value=%d", value);
+
+		if (hcd->speed == HCD_USB3) {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_SS_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		} else {
+			if ((hub->ma_devs[index - 1].port_status &
+			     USB_PORT_STAT_POWER) != 0) {
+				hub->ma_devs[index - 1].port_status |=
+				    (1U << value);
+			}
+		}
+	}
+}
+
+static void mausb_get_port_status(struct usb_hcd *hcd, u16 type_req,
+				  u16 value, u16 index, char *buff,
+				  u16 length)
+{
+	u8 dev_speed;
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	if ((hub->ma_devs[index - 1].port_status &
+				(1 << USB_PORT_FEAT_RESET)) != 0) {
+		dev_info(mausb_host_dev.this_device, "Finished reset hcd->speed=%d",
+			 hcd->speed);
+
+		dev_speed = hub->ma_devs[index - 1].dev_speed;
+		switch (dev_speed) {
+		case LOW_SPEED:
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_LOW_SPEED;
+			break;
+		case HIGH_SPEED:
+			hub->ma_devs[index - 1].port_status |=
+			    USB_PORT_STAT_HIGH_SPEED;
+			break;
+		default:
+			dev_info(mausb_host_dev.this_device, "Not updating port_status for device speed %d",
+				 dev_speed);
+		}
+
+		hub->ma_devs[index - 1].port_status |=
+		    (1 << USB_PORT_FEAT_C_RESET) | USB_PORT_STAT_ENABLE;
+		hub->ma_devs[index - 1].port_status &=
+		    ~(1 << USB_PORT_FEAT_RESET);
+	}
+
+	((__le16 *)buff)[0] = cpu_to_le16(hub->ma_devs[index - 1].port_status);
+	((__le16 *)buff)[1] =
+	    cpu_to_le16(hub->ma_devs[index - 1].port_status >> 16);
+
+	dev_vdbg(mausb_host_dev.this_device, "port_status=%d",
+		 hub->ma_devs[index - 1].port_status);
+}
+
+static void mausb_clear_port_feature(struct usb_hcd *hcd, u16 type_req,
+				     u16 value, u16 index,
+				     char *buff, u16 length)
+{
+	struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	index = ((__u8)(index & 0x00ff));
+
+	switch (value) {
+	case USB_PORT_FEAT_SUSPEND:
+		break;
+	case USB_PORT_FEAT_POWER:
+		dev_vdbg(mausb_host_dev.this_device, "USB_PORT_FEAT_POWER");
+
+		if (hcd->speed == HCD_USB3) {
+			hub->ma_devs[index - 1].port_status &=
+			    ~USB_SS_PORT_STAT_POWER;
+		} else {
+			hub->ma_devs[index - 1].port_status &=
+			    ~USB_PORT_STAT_POWER;
+		}
+		break;
+	case USB_PORT_FEAT_RESET:
+	case USB_PORT_FEAT_C_RESET:
+	default:
+		dev_vdbg(mausb_host_dev.this_device, "Default value: %d",
+			 value);
+
+		hub->ma_devs[index - 1].port_status &= ~(1 << value);
+	}
+}
+
+static void mausb_get_hub_status(struct usb_hcd *hcd, u16 type_req,
+				 u16 value, u16 index, char *buff,
+				 u16 length)
+{
+	*(__le32 *)buff = cpu_to_le32(0);
+}
+
+static int mausb_alloc_dev(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	dev_info(mausb_host_dev.this_device, "Usb device=%p", dev);
+
+	return 1;
+}
+
+static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number;
+	s16	dev_handle;
+	int	status;
+	struct hub_ctx   *hub  = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_dev	    *mdev = NULL;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct mausb_endpoint_ctx   *ep_ctx = dev->ep0.hcpriv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return;
+	}
+
+	mdev  = &hub->ma_devs[port_number];
+
+	usb_device_ctx = mausb_find_usb_device(mdev, dev);
+	if (!usb_device_ctx) {
+		dev_warn(mausb_host_dev.this_device, "device_ctx is not found");
+		return;
+	}
+
+	dev_handle = usb_device_ctx->dev_handle;
+
+	if (ep_ctx) {
+		dev->ep0.hcpriv = NULL;
+		kfree(ep_ctx);
+	} else {
+		dev_warn(mausb_host_dev.this_device, "ep_ctx is NULL: dev_handle=%#x",
+			 dev_handle);
+	}
+
+	rb_erase(&usb_device_ctx->rb_node, &mdev->usb_devices);
+	dev_info(mausb_host_dev.this_device, "USB device deleted device=%p",
+		 usb_device_ctx->dev_addr);
+	kfree(usb_device_ctx);
+}
+
+static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number;
+	int	status;
+	struct hub_ctx	*hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_warn(&dev->dev, "port_number out of range, port_number=%x",
+			 port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx)
+		return -ENODEV;
+
+	dev_info(&dev->dev, "dev_handle=%#x, dev_speed=%#x",
+		 usb_device_ctx->dev_handle, dev->speed);
+
+	if (dev->speed >= USB_SPEED_SUPER)
+		dev_info(&dev->dev, "USB 3.0");
+	else
+		dev_info(&dev->dev, "USB 2.0");
+
+	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED) {
+		status = mausb_enable_device(hcd, dev);
+		if (status < 0)
+			return status;
+	}
+
+	endpoint_ctx = dev->ep0.hcpriv;
+	if (!endpoint_ctx) {
+		dev_err(&dev->dev, "endpoint_ctx is NULL: dev_handle=%#x",
+			usb_device_ctx->dev_handle);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			      struct usb_host_endpoint *endpoint)
+{
+	int	status;
+	u8	port_number;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_dev_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(&dev->dev, "port_number out of range, port_number=%x",
+			port_number);
+		return 0;
+	}
+
+	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_dev_ctx) {
+		dev_warn(&dev->dev, "Device not found");
+		return -ENODEV;
+	}
+
+	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
+	if (!endpoint_ctx)
+		return -ENOMEM;
+
+	endpoint_ctx->dev_handle	= usb_dev_ctx->dev_handle;
+	endpoint_ctx->usb_device_ctx	= usb_dev_ctx;
+	endpoint->hcpriv		= endpoint_ctx;
+
+	return 0;
+}
+
+static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
+			       struct usb_host_endpoint *endpoint)
+{
+	u8	port_number;
+	int	status;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_dev_ctx;
+	struct mausb_endpoint_ctx   *endpoint_ctx = endpoint->hcpriv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(&dev->dev, "port_number out of range, port_number=%x",
+			port_number);
+		return -EINVAL;
+	}
+
+	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!endpoint_ctx) {
+		dev_err(&dev->dev, "Endpoint context doesn't exist");
+		return 0;
+	}
+	if (!usb_dev_ctx) {
+		dev_err(&dev->dev, "Usb device context doesn't exist");
+		return -ENODEV;
+	}
+
+	endpoint->hcpriv = NULL;
+	kfree(endpoint_ctx);
+	return 0;
+}
+
+static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	int status;
+	u8 port_number;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx)
+		return -ENODEV;
+
+	dev_info(mausb_host_dev.this_device, "Device assigned and addressed usb_device_ctx=%p",
+		 usb_device_ctx);
+
+	return 0;
+}
+
+static int mausb_is_hub_device(struct usb_device *dev)
+{
+	return dev->descriptor.bDeviceClass == 0x09;
+}
+
+static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	u8	port_number = 0;
+	int	status	    = 0;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+
+	if (mausb_is_hub_device(dev)) {
+		dev_warn(mausb_host_dev.this_device, "Device is hub");
+		return 0;
+	}
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		dev_warn(mausb_host_dev.this_device, "Device not found");
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
+				   struct usb_tt *tt, gfp_t mem_flags)
+{
+	int	status;
+	u8	port_number;
+	u16 max_exit_latency = 0;
+	u8  mtt = 0;
+	u8  ttt = 0;
+	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	if (dev->speed == USB_SPEED_HIGH) {
+		mtt = tt->multi == 0 ? 1 : 0;
+		ttt = (u8)tt->think_time;
+	}
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return 0;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
+					       dev);
+
+	if (!usb_device_ctx) {
+		dev_err(mausb_host_dev.this_device, "USB device not found");
+		return -ENODEV;
+	}
+
+	if (dev->usb3_lpm_u1_enabled)
+		max_exit_latency = (u16)dev->u1_params.mel;
+	else if (dev->usb3_lpm_u2_enabled)
+		max_exit_latency = (u16)dev->u2_params.mel;
+
+	return 0;
+}
+
+static void mausb_endpoint_reset(struct usb_hcd *hcd,
+				 struct usb_host_endpoint *endpoint)
+{
+	int status;
+	int is_control;
+	int epnum;
+	int is_out;
+	u16	dev_handle;
+	u8	tsp;
+	u8	port_number;
+	struct hub_ctx		    *hub;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+	struct usb_device	    *dev;
+	struct mausb_endpoint_ctx   *ep_ctx;
+
+	ep_ctx = endpoint->hcpriv;
+	if (!ep_ctx)
+		return;
+
+	usb_device_ctx	= ep_ctx->usb_device_ctx;
+	dev_handle	= usb_device_ctx->dev_handle;
+	dev		= usb_device_ctx->dev_addr;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(&dev->dev, "port_number out of range, port_number=%x",
+			port_number);
+		return;
+	}
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	is_control = usb_endpoint_xfer_control(&endpoint->desc);
+	epnum = usb_endpoint_num(&endpoint->desc);
+	is_out = usb_endpoint_dir_out(&endpoint->desc);
+	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
+
+	if (status < 0)
+		return;
+
+	if (status != EUCLEAN) {
+		if (!tsp) {
+			usb_settoggle(dev, epnum, is_out, 0U);
+			if (is_control)
+				usb_settoggle(dev, epnum, !is_out, 0U);
+		}
+
+		return;
+	}
+
+	if (tsp)
+		return;
+
+	dev_info(&dev->dev, "ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x",
+		 status, ep_ctx->ep_handle, dev_handle);
+}
+
+static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
+{
+	int status;
+	u8  port_number;
+	u16 dev_handle;
+	struct hub_ctx		    *hub;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+
+	hub = (struct hub_ctx *)hcd->hcd_priv;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return -EINVAL;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+
+	if (!usb_device_ctx ||
+	    usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
+		return 0;
+
+	dev_handle = usb_device_ctx->dev_handle;
+
+	return 0;
+}
+
+void mausb_clear_hcd_madev(u16 port_number)
+{
+	unsigned long flags = 0;
+
+	if (port_number >= NUMBER_OF_PORTS) {
+		dev_err(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	memset(&mhcd->hcd_hs_ctx->ma_devs[port_number], 0,
+	       sizeof(struct mausb_dev));
+	memset(&mhcd->hcd_ss_ctx->ma_devs[port_number], 0,
+	       sizeof(struct mausb_dev));
+
+	mhcd->connected_ports &= ~(1 << port_number);
+
+	mhcd->hcd_hs_ctx->ma_devs[port_number].port_status =
+							USB_PORT_STAT_POWER;
+	mhcd->hcd_ss_ctx->ma_devs[port_number].port_status =
+							USB_SS_PORT_STAT_POWER;
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+}
diff --git a/drivers/usb/host/mausb/hcd.h b/drivers/usb/host/mausb/hcd.h
index e2374af67663..c0818edc0266 100644
--- a/drivers/usb/host/mausb/hcd.h
+++ b/drivers/usb/host/mausb/hcd.h
@@ -18,9 +18,6 @@
 
 #define RESPONSE_TIMEOUT	5000
 
-#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
-#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
-
 enum mausb_device_type {
 	USBDEVICE = 0,
 	USB20HUB  = 1,
@@ -62,4 +59,87 @@ struct hub_ctx {
 int mausb_init_hcd(void);
 void mausb_deinit_hcd(void);
 
+#define PORT_C_MASK \
+		((USB_PORT_STAT_C_CONNECTION \
+		| USB_PORT_STAT_C_ENABLE \
+		| USB_PORT_STAT_C_SUSPEND \
+		| USB_PORT_STAT_C_OVERCURRENT \
+		| USB_PORT_STAT_C_RESET) << 16)
+
+#define MAUSB_PORT_20_STATUS_CONNECT         0x0001
+#define MAUSB_PORT_20_STATUS_ENABLE          0x0002
+#define MAUSB_PORT_20_STATUS_SUSPEND         0x0004
+#define MAUSB_PORT_20_STATUS_OVER_CURRENT    0x0008
+#define MAUSB_PORT_20_STATUS_RESET           0x0010
+#define MAUSB_PORT_20_STATUS_POWER           0x0100
+#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
+#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
+
+#define MAUSB_CHANGE_PORT_20_STATUS_CONNECT  0x010000
+#define MAUSB_CHANGE_PORT_20_STATUS_RESET    0x100000
+
+/* USB 3.2 specification chapter 10.16.2.6.1 table 10-13 page 440 */
+#define MAUSB_PORT_30_STATUS_CONNECT              0x0001
+#define MAUSB_PORT_30_STATUS_ENABLE               0x0002
+#define MAUSB_PORT_30_STATUS_OVER_CURRENT         0x0008
+#define MAUSB_PORT_30_STATUS_RESET                0x0010
+#define MAUSB_PORT_30_LINK_STATE_U0               0x0000
+#define MAUSB_PORT_30_LINK_STATE_U1               0x0020
+#define MAUSB_PORT_30_LINK_STATE_U2               0x0040
+#define MAUSB_PORT_30_LINK_STATE_U3               0x0060
+#define MAUSB_PORT_30_LINK_STATE_DISABLED         0x0080
+#define MAUSB_PORT_30_LINK_STATE_RX_DETECT        0x00A0
+#define MAUSB_PORT_30_LINK_STATE_INACTIVE         0x00C0
+#define MAUSB_PORT_30_LINK_STATE_POLLING          0x00E0
+#define MAUSB_PORT_30_LINK_STATE_RECOVERY         0x0100
+#define MAUSB_PORT_30_LINK_STATE_HOT_RESET        0x0120
+#define MAUSB_PORT_30_LINK_STATE_COMPLIANCE_MODE  0x0140
+#define MAUSB_PORT_30_LINK_STATE_LOOPBACK         0x0160
+#define MAUSB_PORT_30_STATUS_POWER                0x0200
+#define MAUSB_PORT_30_STATUS_SUPER_SPEED          0x0400
+#define MAUSB_PORT_30_CLEAR_LINK_STATE            0xFE1F
+
+/* USB 3.2 specification chapter 10.16.2.6.2 table 10-14 page 443 */
+#define MAUSB_CHANGE_PORT_30_STATUS_CONNECT              0x010000
+#define MAUSB_CHANGE_PORT_30_STATUS_OVER_CURRENT         0x080000
+#define MAUSB_CHANGE_PORT_30_STATUS_RESET                0x100000
+#define MAUSB_CHANGE_PORT_30_BH_STATUS_RESET             0x200000
+#define MAUSB_CHANGE_PORT_30_LINK_STATE                  0x400000
+#define MAUSB_CHANGE_PORT_30_CONFIG_ERROR                0x800000
+
+/* USB 3.2 specification chapter 10.16.2.4 table 10-10 page 438 */
+#define MAUSB_HUB_30_POWER_GOOD              0x00
+#define MAUSB_HUB_30_LOCAL_POWER_SOURCE_LOST 0x01
+#define MAUSB_HUB_30_OVER_CURRENT            0x02
+
+/* USB 3.2 specification chapter 10.16.2.4 table 10-11 page 438 */
+#define MAUSB_CHANGE_HUB_30_LOCAL_POWER_SOURCE_LOST  0x10000
+#define MAUSB_CHANGE_HUB_30_OVER_CURRENT             0x20000
+
+#define DEV_HANDLE_NOT_ASSIGNED	-1
+
+struct mausb_usb_device_ctx {
+	s32		dev_handle;
+	bool		addressed;
+	void		*dev_addr;
+	struct rb_node	rb_node;
+};
+
+struct mausb_endpoint_ctx {
+	u16	ep_handle;
+	u16	dev_handle;
+	void	*ma_dev;
+	struct mausb_usb_device_ctx *usb_device_ctx;
+};
+
+struct mausb_urb_ctx {
+	struct urb		*urb;
+	struct rb_node		rb_node;
+	struct work_struct	work;
+};
+
+int mausb_probe(struct device *dev);
+
+void mausb_clear_hcd_madev(u16 port_number);
+
 #endif /* __MAUSB_HCD_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 5/8] usb: mausb_host: Introduce PAL processing
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
                         ` (3 preceding siblings ...)
  2020-05-15 12:34       ` [PATCH v6 4/8] usb: mausb_host: Implement initial hub handlers Vladimir Stankovic
@ 2020-05-15 12:34       ` Vladimir Stankovic
  2020-05-15 12:35       ` [PATCH v6 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication Vladimir Stankovic
                         ` (3 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:34 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Protocol adaptation layer (PAL) implementation has been added to
introduce MA-USB structures and logic.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/Makefile        |    1 +
 drivers/usb/host/mausb/hcd.c           |  516 ++++++++++-
 drivers/usb/host/mausb/hcd.h           |   13 +-
 drivers/usb/host/mausb/hpal.c          | 1092 ++++++++++++++++++++++++
 drivers/usb/host/mausb/hpal.h          |  289 +++++++
 drivers/usb/host/mausb/ma_usb.h        |  869 +++++++++++++++++++
 drivers/usb/host/mausb/mausb_address.h |   26 +
 drivers/usb/host/mausb/mausb_core.c    |   11 +
 drivers/usb/host/mausb/mausb_event.h   |  224 +++++
 9 files changed, 3032 insertions(+), 9 deletions(-)
 create mode 100644 drivers/usb/host/mausb/hpal.c
 create mode 100644 drivers/usb/host/mausb/hpal.h
 create mode 100644 drivers/usb/host/mausb/ma_usb.h
 create mode 100644 drivers/usb/host/mausb/mausb_address.h
 create mode 100644 drivers/usb/host/mausb/mausb_event.h

diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
index 4d655cb7fab4..0f9b9be38907 100644
--- a/drivers/usb/host/mausb/Makefile
+++ b/drivers/usb/host/mausb/Makefile
@@ -10,3 +10,4 @@ mausb_host-y := mausb_core.o
 mausb_host-y += utils.o
 mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
+mausb_host-y += hpal.o
diff --git a/drivers/usb/host/mausb/hcd.c b/drivers/usb/host/mausb/hcd.c
index 41b88b6f97b1..26d582ee06e9 100644
--- a/drivers/usb/host/mausb/hcd.c
+++ b/drivers/usb/host/mausb/hcd.c
@@ -132,6 +132,90 @@ void mausb_deinit_hcd(void)
 	}
 }
 
+void mausb_port_has_changed(const enum mausb_device_type device_type,
+			    const enum mausb_device_speed device_speed,
+			    void *ma_dev)
+{
+	struct usb_hcd *hcd;
+	unsigned long flags = 0;
+	struct mausb_device *dev = ma_dev;
+	u16 port_number = dev->port_number;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+		    USB_PORT_STAT_CONNECTION | (1 <<
+						USB_PORT_FEAT_C_CONNECTION);
+
+		if (device_speed == LOW_SPEED) {
+			mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			    MAUSB_PORT_20_STATUS_LOW_SPEED;
+			mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
+			    LOW_SPEED;
+		} else if (device_speed == HIGH_SPEED) {
+			mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			    MAUSB_PORT_20_STATUS_HIGH_SPEED;
+			mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
+			    HIGH_SPEED;
+		}
+
+		hcd = mhcd->hcd_hs_ctx->hcd;
+		mhcd->hcd_hs_ctx->ma_devs[port_number].ma_dev = ma_dev;
+	} else {
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
+		    USB_PORT_STAT_CONNECTION | (1 <<
+						USB_PORT_FEAT_C_CONNECTION);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].dev_speed = SUPER_SPEED;
+
+		hcd = mhcd->hcd_ss_ctx->hcd;
+		mhcd->hcd_ss_ctx->ma_devs[port_number].ma_dev = ma_dev;
+	}
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	usb_hcd_poll_rh_status(hcd);
+}
+
+void mausb_hcd_disconnect(const u16 port_number,
+			  const enum mausb_device_type device_type,
+			  const enum mausb_device_speed device_speed)
+{
+	struct usb_hcd *hcd;
+	unsigned long flags = 0;
+
+	if (port_number >= NUMBER_OF_PORTS) {
+		dev_err(mausb_host_dev.this_device, "port number out of range, port_number=%x",
+			port_number);
+		return;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_CONNECTION);
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_ENABLE);
+		mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
+			(1 << USB_PORT_FEAT_C_CONNECTION);
+		hcd = mhcd->hcd_hs_ctx->hcd;
+	} else {
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_CONNECTION);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
+			~(USB_PORT_STAT_ENABLE);
+		mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
+			(1 << USB_PORT_FEAT_C_CONNECTION);
+		hcd = mhcd->hcd_ss_ctx->hcd;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+	if (!hcd)
+		return;
+
+	usb_hcd_poll_rh_status(hcd);
+}
+
 static const char driver_name[] = "MA-USB host controller";
 
 static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
@@ -168,11 +252,30 @@ static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
 static int mausb_hcd_hub_status(struct usb_hcd *hcd, char *buff);
 static int mausb_hcd_reset(struct usb_hcd *hcd);
 static int mausb_hcd_start(struct usb_hcd *hcd);
+static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				 int status);
+static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				 gfp_t mem_flags);
 static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 				   struct usb_tt *tt, gfp_t mem_flags);
 static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
 static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
 
+static void mausb_print_urb(struct urb *request)
+{
+	dev_vdbg(&request->dev->dev, "URB: urb=%p, ep_handle=%#x, packet_num=%d, setup_dma=%lld, is_setup_packet=%d, is_ep=%d, is_sg=%d, num_sgs=%d, num_mapped_sgs=%d, status=%d, is_transfer_buffer=%d, transfer_buffer_length=%d, is_transfer_dma=%llu, transfer_flags=%d, is_hcpriv=%d",
+		 request, ((struct mausb_endpoint_ctx *)
+			   request->ep->hcpriv)->ep_handle,
+		 request->number_of_packets, request->setup_dma,
+		 request->setup_packet ? 1 : 0, request->ep ? 1 : 0,
+		 request->sg ? 1 : 0, request->num_sgs,
+		 request->num_mapped_sgs, request->status,
+		 request->transfer_buffer ? 1 : 0,
+		 request->transfer_buffer_length,
+		 request->transfer_dma, request->transfer_flags,
+		 (request->ep && request->ep->hcpriv) ? 1 : 0);
+}
+
 static const struct hc_driver mausb_hc_driver = {
 	.description  =  driver_name,
 	.product_desc = driver_name,
@@ -183,6 +286,9 @@ static const struct hc_driver mausb_hc_driver = {
 	.reset = mausb_hcd_reset,
 	.start = mausb_hcd_start,
 
+	.urb_enqueue = mausb_hcd_urb_enqueue,
+	.urb_dequeue = mausb_hcd_urb_dequeue,
+
 	.get_frame_number = mausb_hcd_get_frame_number,
 
 	.hub_status_data   = mausb_hcd_hub_status,
@@ -239,6 +345,25 @@ static int get_root_hub_port_number(struct usb_device *dev, u8 *port_number)
 	return 0;
 }
 
+static int usb_to_mausb_device_speed(u8 speed)
+{
+	switch (speed) {
+	case USB_SPEED_LOW:
+		return MA_USB_SPEED_LOW_SPEED;
+	case USB_SPEED_FULL:
+		return MA_USB_SPEED_FULL_SPEED;
+	case USB_SPEED_WIRELESS:
+	case USB_SPEED_HIGH:
+		return MA_USB_SPEED_HIGH_SPEED;
+	case USB_SPEED_SUPER:
+		return MA_USB_SPEED_SUPER_SPEED;
+	case USB_SPEED_SUPER_PLUS:
+		return MA_USB_SPEED_SUPER_SPEED_PLUS;
+	default:
+		return -EINVAL;
+	}
+}
+
 static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
 							*mdevs, void *dev_addr)
 {
@@ -258,6 +383,31 @@ static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
 	return NULL;
 }
 
+static int mausb_insert_usb_device(struct mausb_dev *mdevs,
+				   struct mausb_usb_device_ctx *usb_device)
+{
+	struct rb_node **new_node = &mdevs->usb_devices.rb_node;
+	struct rb_node *parent = NULL;
+	struct mausb_usb_device_ctx *current_usb_device = NULL;
+
+	while (*new_node) {
+		parent = *new_node;
+		current_usb_device = rb_entry(*new_node,
+					      struct mausb_usb_device_ctx,
+					      rb_node);
+
+		if (usb_device->dev_addr < current_usb_device->dev_addr)
+			new_node = &((*new_node)->rb_left);
+		else if (usb_device->dev_addr > current_usb_device->dev_addr)
+			new_node = &((*new_node)->rb_right);
+		else
+			return -EEXIST;
+	}
+	rb_link_node(&usb_device->rb_node, parent, new_node);
+	rb_insert_color(&usb_device->rb_node, &mdevs->usb_devices);
+	return 0;
+}
+
 static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
 {
 	return 0;
@@ -430,6 +580,118 @@ static int mausb_hcd_hub_control(struct usb_hcd *hcd, u16 type_req,
 	return retval;
 }
 
+static int mausb_validate_urb(struct urb *urb)
+{
+	if (!urb) {
+		dev_err(mausb_host_dev.this_device, "urb is NULL");
+		return -EINVAL;
+	}
+
+	if (!urb->ep->hcpriv) {
+		dev_err(mausb_host_dev.this_device, "urb->ep->hcpriv is NULL");
+		return -EINVAL;
+	}
+
+	if (!urb->ep->enabled) {
+		dev_err(mausb_host_dev.this_device, "Endpoint not enabled");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
+				 gfp_t mem_flags)
+{
+	struct mausb_endpoint_ctx *endpoint_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_urb_ctx	  *urb_ctx;
+	int status = 0;
+
+	if (mausb_validate_urb(urb) < 0) {
+		dev_err(&urb->dev->dev, "Hpal urb enqueue failed");
+		return -EPROTO;
+	}
+
+	endpoint_ctx = urb->ep->hcpriv;
+	ma_dev = endpoint_ctx->ma_dev;
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		dev_err(&urb->dev->dev, "Client is not responsive anymore - finish urb immediately");
+		return -EHOSTDOWN;
+	}
+
+	urb->hcpriv = hcd;
+
+	dev_vdbg(&urb->dev->dev, "ep_handle=%#x, dev_handle=%#x, urb_reject=%d",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+		 atomic_read(&urb->reject));
+
+	status = mausb_insert_urb_in_tree(urb, true);
+	if (status) {
+		dev_err(&urb->dev->dev, "Hpal urb enqueue failed");
+		return status;
+	}
+
+	atomic_inc(&urb->use_count);
+
+	mausb_print_urb(urb);
+
+	status = mausb_data_req_enqueue_event(ma_dev, endpoint_ctx->ep_handle,
+					      urb);
+	if (status < 0) {
+		urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+		atomic_dec(&urb->use_count);
+		if (urb_ctx) {
+			mausb_uninit_data_iterator(&urb_ctx->iterator);
+			kfree(urb_ctx);
+		}
+	}
+
+	return status;
+}
+
+static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
+				 int status)
+{
+	struct mausb_endpoint_ctx *endpoint_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_urb_ctx	  *urb_ctx;
+
+	dev_info(&urb->dev->dev, "Urb=%p", urb);
+
+	urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+	if (!urb_ctx) {
+		dev_warn(mausb_host_dev.this_device, "Urb=%p is not in tree",
+			 urb);
+		return 0;
+	}
+
+	endpoint_ctx = urb->ep->hcpriv;
+	ma_dev	     = endpoint_ctx->ma_dev;
+
+	queue_work(ma_dev->workq, &urb_ctx->work);
+
+	return 0;
+}
+
+void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status)
+{
+	struct mausb_urb_ctx *urb_ctx =
+		mausb_unlink_and_delete_urb_from_tree(urb, status);
+
+	if (urb_ctx) {
+		mausb_uninit_data_iterator(&urb_ctx->iterator);
+		kfree(urb_ctx);
+
+		urb->status	   = status;
+		urb->actual_length = actual_length;
+
+		atomic_dec(&urb->use_count);
+		usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+		return;
+	}
+}
+
 int mausb_probe(struct device *dev)
 {
 	struct mausb_hcd *mausb_hcd;
@@ -693,8 +955,10 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 	u8	port_number;
 	s16	dev_handle;
 	int	status;
+	unsigned long	 flags;
 	struct hub_ctx   *hub  = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_dev	    *mdev = NULL;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct mausb_endpoint_ctx   *ep_ctx = dev->ep0.hcpriv;
 
@@ -707,6 +971,16 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 
 	mdev  = &hub->ma_devs[port_number];
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
+			port_number);
+		return;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(mdev, dev);
 	if (!usb_device_ctx) {
 		dev_warn(mausb_host_dev.this_device, "device_ctx is not found");
@@ -715,6 +989,13 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 
 	dev_handle = usb_device_ctx->dev_handle;
 
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		dev_err(mausb_host_dev.this_device, "Client is not responsive anymore - free usbdevice immediately");
+		dev->ep0.hcpriv = NULL;
+		kfree(ep_ctx);
+		goto free_dev;
+	}
+
 	if (ep_ctx) {
 		dev->ep0.hcpriv = NULL;
 		kfree(ep_ctx);
@@ -723,17 +1004,61 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 			 dev_handle);
 	}
 
+free_dev:
+	if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
+		dev_info(mausb_host_dev.this_device, "All usb devices destroyed - proceed with disconnecting");
+		queue_work(ma_dev->workq, &ma_dev->socket_disconnect_work);
+	}
+
 	rb_erase(&usb_device_ctx->rb_node, &mdev->usb_devices);
 	dev_info(mausb_host_dev.this_device, "USB device deleted device=%p",
 		 usb_device_ctx->dev_addr);
 	kfree(usb_device_ctx);
+
+	if (kref_put(&ma_dev->refcount, mausb_release_ma_dev_async))
+		mausb_clear_hcd_madev(port_number);
+}
+
+static struct mausb_usb_device_ctx *
+mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
+		       struct mausb_device *ma_dev, u16 port_number,
+		       int *status)
+{
+	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+
+	usb_device_ctx = kzalloc(sizeof(*usb_device_ctx), GFP_ATOMIC);
+	if (!usb_device_ctx) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	usb_device_ctx->dev_addr   = dev;
+	usb_device_ctx->dev_handle = DEV_HANDLE_NOT_ASSIGNED;
+	usb_device_ctx->addressed  = false;
+
+	if (mausb_insert_usb_device(&hub->ma_devs[port_number],
+				    usb_device_ctx)) {
+		dev_warn(&dev->dev, "device_ctx already exists");
+		kfree(usb_device_ctx);
+		*status = -EEXIST;
+		return NULL;
+	}
+
+	kref_get(&ma_dev->refcount);
+	dev_info(&dev->dev, "New USB device added device=%p",
+		 usb_device_ctx->dev_addr);
+	atomic_inc(&ma_dev->num_of_usb_devices);
+
+	return usb_device_ctx;
 }
 
 static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 {
 	u8	port_number;
 	int	status;
+	unsigned long	flags;
 	struct hub_ctx	*hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx;
 
@@ -744,9 +1069,23 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
-	if (!usb_device_ctx)
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_warn(&dev->dev, "MAUSB device not found on port_number=%d",
+			 port_number);
 		return -ENODEV;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
+							port_number, &status);
+		if (!usb_device_ctx)
+			return status;
+	}
 
 	dev_info(&dev->dev, "dev_handle=%#x, dev_speed=%#x",
 		 usb_device_ctx->dev_handle, dev->speed);
@@ -777,9 +1116,13 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	int	status;
 	u8	port_number;
+	struct ma_usb_ephandlereq_desc_ss  descriptor_ss;
+	struct ma_usb_ephandlereq_desc_std descriptor;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx;
+	unsigned long flags = 0;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -788,6 +1131,16 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return 0;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(&dev->dev, "MAUSB device not found on port_number=%d",
+			port_number);
+		return -ENODEV;
+	}
+
 	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 	if (!usb_dev_ctx) {
 		dev_warn(&dev->dev, "Device not found");
@@ -800,8 +1153,17 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 
 	endpoint_ctx->dev_handle	= usb_dev_ctx->dev_handle;
 	endpoint_ctx->usb_device_ctx	= usb_dev_ctx;
+	endpoint_ctx->ma_dev		= ma_dev;
 	endpoint->hcpriv		= endpoint_ctx;
 
+	if (dev->speed >= USB_SPEED_SUPER) {
+		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
+						    &endpoint->desc,
+						    &endpoint->ss_ep_comp);
+	} else {
+		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+	}
+
 	return 0;
 }
 
@@ -811,8 +1173,10 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 	u8	port_number;
 	int	status;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
 	struct mausb_endpoint_ctx   *endpoint_ctx = endpoint->hcpriv;
+	unsigned long flags = 0;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -821,6 +1185,16 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(&dev->dev, "MAUSB device not found on port_number=%d",
+			port_number);
+		return -ENODEV;
+	}
+
 	usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!endpoint_ctx) {
@@ -837,12 +1211,78 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 	return 0;
 }
 
+static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
+					  struct usb_device *dev,
+					  struct hub_ctx *hub,
+					  struct mausb_device *ma_dev,
+					  struct mausb_usb_device_ctx
+					  *usb_device_ctx)
+{
+	u8 port_number;
+	int status;
+	int dev_speed;
+	u16 hub_dev_handle		= 0;
+	u16 parent_hs_hub_dev_handle	= 0;
+	u16 parent_hs_hub_port		= 0;
+	struct usb_device		   *first_hub_device = dev;
+	struct mausb_usb_device_ctx	   *hub_device_ctx;
+	struct mausb_endpoint_ctx	   *endpoint_ctx;
+	struct ma_usb_ephandlereq_desc_std descriptor;
+
+	status = get_root_hub_port_number(dev, &port_number);
+	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
+		dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
+			port_number);
+		return -EINVAL;
+	}
+
+	while (first_hub_device->parent->parent)
+		first_hub_device = first_hub_device->parent;
+
+	hub_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
+					       first_hub_device);
+	if (hub_device_ctx)
+		hub_dev_handle = hub_device_ctx->dev_handle;
+
+	if ((dev->speed == USB_SPEED_LOW || dev->speed == USB_SPEED_FULL) &&
+	    first_hub_device->speed == USB_SPEED_HIGH) {
+		parent_hs_hub_dev_handle =
+			mausb_find_usb_device(&hub->ma_devs[port_number],
+					      dev->parent)->dev_handle;
+		parent_hs_hub_port = dev->parent->portnum;
+	}
+
+	dev_speed = usb_to_mausb_device_speed(dev->speed);
+	dev_info(mausb_host_dev.this_device, "start... mausb_devspeed=%d, route=%#x, port_number=%d",
+		 dev_speed, dev->route, port_number);
+
+	if (dev_speed == -EINVAL) {
+		dev_err(mausb_host_dev.this_device, "bad dev_speed");
+		return -EINVAL;
+	}
+
+	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
+	if (!endpoint_ctx)
+		return -ENOMEM;
+
+	endpoint_ctx->dev_handle     = usb_device_ctx->dev_handle;
+	endpoint_ctx->ma_dev	     = ma_dev;
+	endpoint_ctx->usb_device_ctx = usb_device_ctx;
+	dev->ep0.hcpriv		     = endpoint_ctx;
+
+	mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
+
+	return 0;
+}
+
 static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 {
 	int status;
 	u8 port_number;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
+	unsigned long flags;
 
 	status = get_root_hub_port_number(dev, &port_number);
 	if (status < 0 || port_number >= NUMBER_OF_PORTS) {
@@ -851,13 +1291,30 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
-	if (!usb_device_ctx)
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
+			port_number);
 		return -ENODEV;
+	}
+
+	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
+	if (!usb_device_ctx) {
+		usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
+							port_number, &status);
+		if (!usb_device_ctx)
+			return status;
+	}
 
 	dev_info(mausb_host_dev.this_device, "Device assigned and addressed usb_device_ctx=%p",
 		 usb_device_ctx);
 
+	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
+		return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
+						      usb_device_ctx);
 	return 0;
 }
 
@@ -871,7 +1328,9 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 	u8	port_number = 0;
 	int	status	    = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev = NULL;
 	struct mausb_usb_device_ctx *usb_device_ctx = NULL;
+	unsigned long flags = 0;
 
 	if (mausb_is_hub_device(dev)) {
 		dev_warn(mausb_host_dev.this_device, "Device is hub");
@@ -885,6 +1344,16 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
+			port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 	if (!usb_device_ctx) {
 		dev_warn(mausb_host_dev.this_device, "Device not found");
@@ -899,10 +1368,12 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	int	status;
 	u8	port_number;
+	unsigned long flags;
 	u16 max_exit_latency = 0;
 	u8  mtt = 0;
 	u8  ttt = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 
 	if (dev->speed == USB_SPEED_HIGH) {
@@ -917,6 +1388,16 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 		return 0;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
+			port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
 					       dev);
 
@@ -940,10 +1421,12 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	int is_control;
 	int epnum;
 	int is_out;
+	unsigned long flags;
 	u16	dev_handle;
 	u8	tsp;
 	u8	port_number;
 	struct hub_ctx		    *hub;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct usb_device	    *dev;
 	struct mausb_endpoint_ctx   *ep_ctx;
@@ -964,14 +1447,21 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	}
 	hub = (struct hub_ctx *)hcd->hcd_priv;
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(&dev->dev, "MAUSB device not found on port_number=%d",
+			port_number);
+		return;
+	}
+
 	is_control = usb_endpoint_xfer_control(&endpoint->desc);
 	epnum = usb_endpoint_num(&endpoint->desc);
 	is_out = usb_endpoint_dir_out(&endpoint->desc);
 	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
 
-	if (status < 0)
-		return;
-
 	if (status != EUCLEAN) {
 		if (!tsp) {
 			usb_settoggle(dev, epnum, is_out, 0U);
@@ -994,7 +1484,9 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 	int status;
 	u8  port_number;
 	u16 dev_handle;
+	unsigned long flags;
 	struct hub_ctx		    *hub;
+	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
 
 	hub = (struct hub_ctx *)hcd->hcd_priv;
@@ -1006,6 +1498,16 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
+	spin_lock_irqsave(&mhcd->lock, flags);
+	ma_dev = hub->ma_devs[port_number].ma_dev;
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	if (!ma_dev) {
+		dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
+			port_number);
+		return -ENODEV;
+	}
+
 	usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
 
 	if (!usb_device_ctx ||
diff --git a/drivers/usb/host/mausb/hcd.h b/drivers/usb/host/mausb/hcd.h
index c0818edc0266..7a20264aaa35 100644
--- a/drivers/usb/host/mausb/hcd.h
+++ b/drivers/usb/host/mausb/hcd.h
@@ -9,13 +9,13 @@
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 
+#include "hpal.h"
+
 #define DEVICE_NAME "mausb_host_hcd"
 #define CLASS_NAME "mausb"
 
 #define NUMBER_OF_PORTS		15
-
 #define MAX_USB_DEVICE_DEPTH	6
-
 #define RESPONSE_TIMEOUT	5000
 
 enum mausb_device_type {
@@ -59,6 +59,13 @@ struct hub_ctx {
 int mausb_init_hcd(void);
 void mausb_deinit_hcd(void);
 
+void mausb_port_has_changed(const enum mausb_device_type device_type,
+			    const enum mausb_device_speed device_speed,
+			    void *ma_dev);
+void mausb_hcd_disconnect(const u16 port_number,
+			  const enum mausb_device_type device_type,
+			  const enum mausb_device_speed device_speed);
+
 #define PORT_C_MASK \
 		((USB_PORT_STAT_C_CONNECTION \
 		| USB_PORT_STAT_C_ENABLE \
@@ -134,11 +141,13 @@ struct mausb_endpoint_ctx {
 
 struct mausb_urb_ctx {
 	struct urb		*urb;
+	struct mausb_data_iter	iterator;
 	struct rb_node		rb_node;
 	struct work_struct	work;
 };
 
 int mausb_probe(struct device *dev);
+void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status);
 
 void mausb_clear_hcd_madev(u16 port_number);
 
diff --git a/drivers/usb/host/mausb/hpal.c b/drivers/usb/host/mausb/hpal.c
new file mode 100644
index 000000000000..5368c500a320
--- /dev/null
+++ b/drivers/usb/host/mausb/hpal.c
@@ -0,0 +1,1092 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal.h"
+
+#include <linux/circ_buf.h>
+
+#include "hcd.h"
+#include "utils.h"
+
+struct mss mss;
+
+static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
+				void *data);
+static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
+static int mausb_start_heartbeat_timer(void);
+
+static inline struct mausb_urb_ctx *__mausb_find_urb_in_tree(struct urb *urb)
+{
+	struct rb_node *node = mhcd->mausb_urbs.rb_node;
+
+	while (node) {
+		struct mausb_urb_ctx *urb_ctx =
+		    rb_entry(node, struct mausb_urb_ctx, rb_node);
+
+		if (urb < urb_ctx->urb)
+			node = urb_ctx->rb_node.rb_left;
+		else if (urb > urb_ctx->urb)
+			node = urb_ctx->rb_node.rb_right;
+		else
+			return urb_ctx;
+	}
+	return NULL;
+}
+
+struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb)
+{
+	unsigned long flags = 0;
+	struct mausb_urb_ctx *urb_ctx;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	urb_ctx =  __mausb_find_urb_in_tree(urb);
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return urb_ctx;
+}
+
+static int mausb_insert_urb_ctx_in_tree(struct mausb_urb_ctx *urb_ctx)
+{
+	struct rb_node **new_node = &mhcd->mausb_urbs.rb_node;
+	struct rb_node *parent = NULL;
+	struct mausb_urb_ctx *current_urb = NULL;
+
+	while (*new_node) {
+		parent = *new_node;
+		current_urb = rb_entry(*new_node, struct mausb_urb_ctx,
+				       rb_node);
+
+		if (urb_ctx->urb < current_urb->urb)
+			new_node = &((*new_node)->rb_left);
+		else if (urb_ctx->urb > current_urb->urb)
+			new_node = &((*new_node)->rb_right);
+		else
+			return -EEXIST;
+	}
+	rb_link_node(&urb_ctx->rb_node, parent, new_node);
+	rb_insert_color(&urb_ctx->rb_node, &mhcd->mausb_urbs);
+	return 0;
+}
+
+static void mausb_delete_urb_ctx_from_tree(struct mausb_urb_ctx *urb_ctx)
+{
+	rb_erase(&urb_ctx->rb_node, &mhcd->mausb_urbs);
+}
+
+static struct mausb_urb_ctx *mausb_create_urb_ctx(struct urb *urb, int *status)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+
+	if (!urb) {
+		dev_err(mausb_host_dev.this_device, "Urb is NULL");
+		*status = -EINVAL;
+		return NULL;
+	}
+
+	urb_ctx = kzalloc(sizeof(*urb_ctx), GFP_ATOMIC);
+	if (!urb_ctx) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	urb_ctx->urb = urb;
+	INIT_WORK(&urb_ctx->work, mausb_execute_urb_dequeue);
+
+	return urb_ctx;
+}
+
+int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep)
+{
+	unsigned long flags;
+	int status = 0;
+
+	struct mausb_urb_ctx *urb_ctx = mausb_create_urb_ctx(urb, &status);
+
+	if (!urb_ctx)
+		return status;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	if (link_urb_to_ep) {
+		status = usb_hcd_link_urb_to_ep(urb->hcpriv, urb);
+		if (status) {
+			spin_unlock_irqrestore(&mhcd->lock, flags);
+			dev_err(mausb_host_dev.this_device, "Error %d while linking urb to hcd_endpoint",
+				status);
+			kfree(urb_ctx);
+			return status;
+		}
+	}
+
+	if (mausb_insert_urb_ctx_in_tree(urb_ctx)) {
+		kfree(urb_ctx);
+		if (link_urb_to_ep)
+			usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		dev_err(mausb_host_dev.this_device, "Urb_ctx insertion failed");
+		return -EEXIST;
+	}
+
+	mausb_init_data_iterator(&urb_ctx->iterator, urb->transfer_buffer,
+				 urb->transfer_buffer_length, urb->sg,
+				 (unsigned int)urb->num_sgs,
+				 usb_urb_dir_in(urb));
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return 0;
+}
+
+static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx,
+					 bool link_urb_to_ep)
+{
+	unsigned long flags;
+	int status;
+
+	if (!urb_ctx)
+		return false;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+	if (link_urb_to_ep) {
+		status = usb_hcd_link_urb_to_ep(urb_ctx->urb->hcpriv,
+						urb_ctx->urb);
+		if (status) {
+			spin_unlock_irqrestore(&mhcd->lock, flags);
+			dev_err(mausb_host_dev.this_device, "Error %d while linking urb to hcd_endpoint",
+				status);
+			return false;
+		}
+	}
+
+	if (mausb_insert_urb_ctx_in_tree(urb_ctx)) {
+		if (link_urb_to_ep)
+			usb_hcd_unlink_urb_from_ep(urb_ctx->urb->hcpriv,
+						   urb_ctx->urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		dev_err(mausb_host_dev.this_device, "Urb_ctx insertion failed");
+		return false;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	return true;
+}
+
+/*After this function call only valid thing to do with urb is to give it back*/
+struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
+							    int status)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+	unsigned long flags;
+	int ret;
+
+	if (!urb) {
+		dev_warn(mausb_host_dev.this_device, "URB is NULL");
+		return NULL;
+	}
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	urb_ctx = __mausb_find_urb_in_tree(urb);
+
+	if (!urb_ctx) {
+		dev_warn(mausb_host_dev.this_device, "Urb=%p not in tree", urb);
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		return NULL;
+	}
+
+	ret = usb_hcd_check_unlink_urb(urb->hcpriv, urb, status);
+
+	if (ret == -EIDRM)
+		dev_warn(&urb->dev->dev, "Urb=%p is already unlinked", urb);
+	else
+		usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb);
+
+	mausb_delete_urb_ctx_from_tree(urb_ctx);
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	dev_vdbg(&urb->dev->dev, "Urb=%p is removed from tree", urb);
+
+	return urb_ctx;
+}
+
+void mausb_release_event_resources(struct mausb_event *event)
+{
+	struct ma_usb_hdr_common *receive_buffer = (struct ma_usb_hdr_common *)
+						    event->data.recv_buf;
+
+	kfree(receive_buffer);
+}
+
+void mausb_complete_urb(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+
+	dev_vdbg(mausb_host_dev.this_device, "URB complete request, transfer_size=%d, rem_transfer_size=%d, status=%d",
+		 event->data.transfer_size, event->data.rem_transfer_size,
+		 event->status);
+	mausb_complete_request(urb,
+			       event->data.transfer_size -
+			       event->data.rem_transfer_size,
+			       event->status);
+}
+
+static int mausb_get_first_free_port_number(u16 *port_number)
+{
+	(*port_number) = 0;
+	while ((mhcd->connected_ports & (1 << *port_number)) != 0 &&
+	       *port_number < NUMBER_OF_PORTS)
+		++(*port_number);
+
+	if (*port_number == NUMBER_OF_PORTS)
+		return -EINVAL;
+
+	mhcd->connected_ports |= (1 << *port_number);
+
+	return 0;
+}
+
+static inline void mausb_port_has_changed_event(struct mausb_device *dev,
+						struct mausb_event *event)
+{
+	int status;
+	u16 port_number;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	status = mausb_get_first_free_port_number(&port_number);
+	if (status < 0) {
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+		dev_err(mausb_host_dev.this_device, "There is no free port, schedule delete ma_dev");
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	dev->dev_type	   = event->port_changed.dev_type;
+	dev->dev_speed	   = event->port_changed.dev_speed;
+	dev->lse	   = event->port_changed.lse;
+	dev->dev_connected = 1;
+	dev->port_number   = port_number;
+
+	mausb_port_has_changed(event->port_changed.dev_type,
+			       event->port_changed.dev_speed, dev);
+
+	if ((enum mausb_device_type)event->port_changed.dev_type == USB30HUB)
+		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
+}
+
+static void mausb_heartbeat_timer_func(struct timer_list *timer)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	if (mausb_start_heartbeat_timer() < 0) {
+		dev_err(mausb_host_dev.this_device, "App is unresponsive - disconnecting devices");
+		spin_lock_irqsave(&mss.lock, flags);
+
+		/* Reset connected clients */
+		mss.client_connected = false;
+		mss.missed_heartbeats = 0;
+
+		list_for_each_entry(dev, &mss.madev_list, list_entry) {
+			dev_vdbg(mausb_host_dev.this_device, "Enqueue heartbeat_work madev_addr=%x",
+				 dev->madev_addr);
+			queue_work(dev->workq, &dev->heartbeat_work);
+		}
+
+		complete(&mss.client_stopped);
+		spin_unlock_irqrestore(&mss.lock, flags);
+	}
+}
+
+void mausb_release_ma_dev_async(struct kref *kref)
+{
+	struct mausb_device *dev = container_of(kref, struct mausb_device,
+						refcount);
+
+	dev_info(mausb_host_dev.this_device, "Scheduling work for MAUSB device to be deleted");
+
+	schedule_work(&dev->madev_delete_work);
+}
+
+int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
+				  u16 num_of_completed)
+{
+	unsigned long flags;
+	struct mausb_device *dev;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(madev_addr);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -EINVAL;
+	}
+
+	spin_lock(&dev->num_of_user_events_lock);
+	dev->num_of_user_events += num_of_events;
+	dev->num_of_completed_events += num_of_completed;
+	spin_unlock(&dev->num_of_user_events_lock);
+	queue_work(dev->workq, &dev->work);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+}
+
+int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
+				 struct urb *request)
+{
+	struct mausb_event mausb_event;
+
+	mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
+	mausb_event.status = 0;
+
+	mausb_event.data.transfer_type =
+		mausb_transfer_type_from_usb(&request->ep->desc);
+	mausb_event.data.device_id	= dev->id;
+	mausb_event.data.ep_handle	= ep_handle;
+	mausb_event.data.urb		= (uintptr_t)request;
+	mausb_event.data.setup_packet	=
+		(usb_endpoint_xfer_control(&request->ep->desc) &&
+			request->setup_packet);
+	mausb_event.data.transfer_size	= request->transfer_buffer_length;
+	mausb_event.data.direction	= (usb_urb_dir_in(request) ?
+						MAUSB_DATA_MSG_DIRECTION_IN :
+						MAUSB_DATA_MSG_DIRECTION_OUT);
+	mausb_event.data.transfer_size +=
+		((mausb_event.data.direction == MAUSB_DATA_MSG_DIRECTION_OUT &&
+			mausb_event.data.setup_packet) ?
+				MAUSB_CONTROL_SETUP_SIZE : 0);
+	mausb_event.data.rem_transfer_size = mausb_event.data.transfer_size;
+	mausb_event.data.transfer_flags	   = request->transfer_flags;
+	mausb_event.data.transfer_eot	   = false;
+	mausb_event.data.isoch_seg_num	   = (u32)request->number_of_packets;
+	mausb_event.data.recv_buf	   = 0;
+	mausb_event.data.payload_size	   =
+		(usb_endpoint_xfer_isoc(&request->ep->desc) &&
+		 usb_endpoint_dir_out(&request->ep->desc)) ?
+		(request->iso_frame_desc[request->number_of_packets - 1]
+								.offset +
+		 request->iso_frame_desc[request->number_of_packets - 1]
+								.length) : 0;
+
+	if (mausb_event.data.setup_packet) {
+		memcpy(mausb_event.data.hdr_ack, request->setup_packet,
+		       MAUSB_CONTROL_SETUP_SIZE);
+		memcpy(shift_ptr(mausb_event.data.hdr_ack,
+				 MAUSB_CONTROL_SETUP_SIZE),
+		       &request->dev->route, sizeof(request->dev->route));
+	}
+
+	return 0;
+}
+
+void mausb_complete_request(struct urb *urb, u32 actual_length, int status)
+{
+	mausb_hcd_urb_complete(urb, actual_length, status);
+}
+
+int mausb_signal_event(struct mausb_device *dev,
+		       struct mausb_event *event, u64 event_id)
+{
+	unsigned long flags;
+	struct mausb_completion *mausb_completion;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_for_each_entry(mausb_completion, &dev->completion_events,
+			    list_entry) {
+		if (mausb_completion->event_id == event_id) {
+			memcpy(mausb_completion->mausb_event, event,
+			       sizeof(*event));
+			complete(mausb_completion->completion_event);
+			spin_unlock_irqrestore(&dev->completion_events_lock,
+					       flags);
+			return 0;
+		}
+	}
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+
+	return -ETIMEDOUT;
+}
+
+void mausb_reset_connection_timer(struct mausb_device *dev)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&dev->connection_timer_lock, flags);
+	dev->receive_failures_num = 0;
+
+	mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+	spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+}
+
+static int mausb_start_heartbeat_timer(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (++mss.missed_heartbeats > MAUSB_MAX_MISSED_HEARTBEATS) {
+		dev_err(mausb_host_dev.this_device, "Missed more than %d heartbeats",
+			MAUSB_MAX_MISSED_HEARTBEATS);
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+	mod_timer(&mss.heartbeat_timer,
+		  jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
+	return 0;
+}
+
+void mausb_reset_heartbeat_cnt(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	mss.missed_heartbeats = 0;
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
+{
+	struct mausb_urb_ctx *urb_ctx =
+			container_of(dequeue_work, struct mausb_urb_ctx, work);
+	struct urb		  *urb = urb_ctx->urb;
+	struct mausb_endpoint_ctx *ep_ctx;
+	struct mausb_device	  *ma_dev;
+	struct mausb_event	  mausb_event;
+	int status = 0;
+
+	ep_ctx = urb->ep->hcpriv;
+	ma_dev = ep_ctx->ma_dev;
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		dev_err(mausb_host_dev.this_device, "Client is not responsive anymore - finish urb immediately urb=%p, ep_handle=%#x, dev_handle=%#x",
+			urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
+		goto complete_urb;
+	}
+
+	dev_vdbg(mausb_host_dev.this_device, "urb=%p, ep_handle=%#x, dev_handle=%#x",
+		 urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
+
+	memset(&mausb_event, 0, sizeof(mausb_event));
+
+	mausb_event.type   = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER;
+	mausb_event.status = 0;
+
+	mausb_event.data.transfer_type =
+				mausb_transfer_type_from_usb(&urb->ep->desc);
+	mausb_event.data.device_id     = ma_dev->id;
+	mausb_event.data.ep_handle     = ep_ctx->ep_handle;
+	mausb_event.data.urb	       = (uintptr_t)urb;
+	mausb_event.data.direction     = (usb_urb_dir_in(urb) ?
+						MAUSB_DATA_MSG_DIRECTION_IN :
+						MAUSB_DATA_MSG_DIRECTION_OUT);
+
+	if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) {
+		dev_alert(mausb_host_dev.this_device, "Failed to insert in tree urb=%p ep_handle=%#x, status=%d",
+			  urb, mausb_event.data.ep_handle, status);
+		goto complete_urb;
+	}
+
+	return;
+
+complete_urb:
+
+	/* Deallocate urb_ctx */
+	mausb_uninit_data_iterator(&urb_ctx->iterator);
+	kfree(urb_ctx);
+
+	urb->status	   = -EPROTO;
+	urb->actual_length = 0;
+	atomic_dec(&urb->use_count);
+	usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+}
+
+void mausb_initialize_mss(void)
+{
+	spin_lock_init(&mss.lock);
+	INIT_LIST_HEAD(&mss.madev_list);
+	INIT_LIST_HEAD(&mss.available_ring_buffers);
+
+	init_completion(&mss.empty);
+	complete(&mss.empty);
+	init_completion(&mss.rings_events.mausb_ring_has_events);
+	atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 0);
+	mss.deinit_in_progress	= false;
+	mss.ring_buffer_id	= 0;
+	mss.client_connected = false;
+	mss.missed_heartbeats = 0;
+	init_completion(&mss.client_stopped);
+	atomic_set(&mss.num_of_transitions_to_sleep, 0);
+
+	timer_setup(&mss.heartbeat_timer, mausb_heartbeat_timer_func, 0);
+}
+
+void mausb_deinitialize_mss(void)
+{
+	struct mausb_device *dev = NULL;
+	unsigned long flags = 0;
+	unsigned long timeout =
+			msecs_to_jiffies(MAUSB_CLIENT_STOPPED_TIMEOUT_MS);
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	mss.deinit_in_progress = true;
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		dev_dbg(mausb_host_dev.this_device, "Enqueue mausb_hcd_disconnect_work madev_addr=%x",
+			dev->madev_addr);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	wait_for_completion(&mss.empty);
+	dev_dbg(mausb_host_dev.this_device, "Waiting for completion on disconnect_event ended");
+
+	timeout = wait_for_completion_timeout(&mss.client_stopped, timeout);
+	dev_info(mausb_host_dev.this_device, "Remaining time after waiting for stopping client %ld",
+		 timeout);
+}
+
+int mausb_register_power_state_listener(void)
+{
+	dev_info(mausb_host_dev.this_device, "Registering power states listener");
+
+	mhcd->power_state_listener.notifier_call = mausb_power_state_cb;
+	return register_pm_notifier(&mhcd->power_state_listener);
+}
+
+void mausb_unregister_power_state_listener(void)
+{
+	dev_info(mausb_host_dev.this_device, "Un-registering power states listener");
+
+	unregister_pm_notifier(&mhcd->power_state_listener);
+}
+
+static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
+				void *data)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	dev_info(mausb_host_dev.this_device, "Power state callback action = %ld",
+		 action);
+	if (action == PM_SUSPEND_PREPARE || action == PM_HIBERNATION_PREPARE) {
+		/* Stop heartbeat timer */
+		del_timer_sync(&mss.heartbeat_timer);
+		dev_info(mausb_host_dev.this_device, "Saving state before sleep");
+		spin_lock_irqsave(&mss.lock, flags);
+		if (!list_empty(&mss.madev_list))
+			atomic_inc(&mss.num_of_transitions_to_sleep);
+
+		list_for_each_entry(dev, &mss.madev_list, list_entry) {
+			dev_info(mausb_host_dev.this_device, "Enqueue heartbeat_work madev_addr=%x",
+				 dev->madev_addr);
+			queue_work(dev->workq, &dev->heartbeat_work);
+		}
+
+		spin_unlock_irqrestore(&mss.lock, flags);
+	} else if (action == PM_POST_SUSPEND || action == PM_POST_HIBERNATION) {
+		mausb_reset_heartbeat_cnt();
+		/* Start hearbeat timer */
+		mod_timer(&mss.heartbeat_timer, jiffies +
+			  msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+	}
+	return NOTIFY_OK;
+}
+
+static void mausb_populate_standard_ep_descriptor(struct usb_ep_desc *std_desc,
+						  struct usb_endpoint_descriptor
+						  *usb_std_desc)
+{
+	std_desc->bLength	   = usb_std_desc->bLength;
+	std_desc->bDescriptorType  = usb_std_desc->bDescriptorType;
+	std_desc->bEndpointAddress = usb_std_desc->bEndpointAddress;
+	std_desc->bmAttributes	   = usb_std_desc->bmAttributes;
+	std_desc->wMaxPacketSize   = usb_std_desc->wMaxPacketSize;
+	std_desc->bInterval	   = usb_std_desc->bInterval;
+}
+
+static void
+mausb_populate_superspeed_ep_descriptor(struct usb_ss_ep_comp_desc *ss_desc,
+					struct usb_ss_ep_comp_descriptor*
+					usb_ss_desc)
+{
+	ss_desc->bLength	   = usb_ss_desc->bLength;
+	ss_desc->bDescriptorType   = usb_ss_desc->bDescriptorType;
+	ss_desc->bMaxBurst	   = usb_ss_desc->bMaxBurst;
+	ss_desc->bmAttributes	   = usb_ss_desc->bmAttributes;
+	ss_desc->wBytesPerInterval = usb_ss_desc->wBytesPerInterval;
+}
+
+void
+mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *std_desc,
+				  struct usb_endpoint_descriptor *usb_std_desc)
+{
+	mausb_populate_standard_ep_descriptor(&std_desc->usb20, usb_std_desc);
+}
+
+void
+mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *ss_desc,
+				    struct usb_endpoint_descriptor *
+				    usb_std_desc,
+				    struct usb_ss_ep_comp_descriptor *
+				    usb_ss_desc)
+{
+	mausb_populate_standard_ep_descriptor(&ss_desc->usb20, usb_std_desc);
+	mausb_populate_superspeed_ep_descriptor(&ss_desc->usb31, usb_ss_desc);
+}
+
+struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr)
+{
+	struct mausb_device *dev = NULL;
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		if (dev->madev_addr == madev_addr)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static inline
+struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
+					    enum mausb_channel channel)
+{
+	if (channel >= MAUSB_CHANNEL_MAP_LENGTH)
+		return NULL;
+
+	return ma_dev->channel_map[channel];
+}
+
+int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num,
+		    struct mausb_kvec_data_wrapper *data)
+{
+	struct mausb_ip_ctx *channel = mausb_get_data_channel(dev, channel_num);
+	int status = 0;
+
+	if (!channel)
+		return -ECHRNG;
+
+	status = mausb_ip_send(channel, data);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Send failed. Disconnecting... status=%d",
+			status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	return status;
+}
+
+int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct ma_usb_hdr_common *ack_hdr;
+	struct kvec kvec;
+	struct mausb_kvec_data_wrapper data_to_send;
+	enum mausb_channel channel;
+
+	ack_hdr = (struct ma_usb_hdr_common *)(&event->data.hdr_ack);
+
+	data_to_send.kvec	    = &kvec;
+	data_to_send.kvec->iov_base = ack_hdr;
+	data_to_send.kvec->iov_len  = ack_hdr->length;
+	data_to_send.kvec_num	    = 1;
+	data_to_send.length	    = ack_hdr->length;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
+
+	if (event->status != 0) {
+		dev_err(mausb_host_dev.this_device, "Event %d failed with status %d",
+			event->type, event->status);
+		mausb_complete_urb(event);
+		return event->status;
+	}
+
+	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
+
+	if (!urb_ctx) {
+		/* Transfer will be deleted from dequeue task */
+		dev_warn(mausb_host_dev.this_device, "Urb is already cancelled for event=%d",
+			 event->type);
+		return status;
+	}
+
+	return status;
+}
+
+int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	int status = 0;
+	struct mausb_urb_ctx *urb_ctx;
+
+	dev_vdbg(mausb_host_dev.this_device, "Direction=%d",
+		 event->data.direction);
+
+	if (!mausb_isoch_data_event(event)) {
+		status = mausb_send_transfer_ack(dev, event);
+		if (status < 0) {
+			dev_warn(mausb_host_dev.this_device, "Sending acknowledgment failed");
+			goto cleanup;
+		}
+	}
+
+	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
+
+	if (!urb_ctx)
+		dev_warn(mausb_host_dev.this_device, "Urb is already cancelled");
+
+cleanup:
+	mausb_release_event_resources(event);
+	return status;
+}
+
+int mausb_add_data_chunk(void *buffer, u32 buffer_size,
+			 struct list_head *chunks_list)
+{
+	struct mausb_payload_chunk *data_chunk;
+
+	data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+	if (!data_chunk)
+		return -ENOMEM;
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	INIT_LIST_HEAD(&data_chunk->list_entry);
+
+	data_chunk->kvec.iov_base = buffer;
+	data_chunk->kvec.iov_len  = buffer_size;
+	list_add_tail(&data_chunk->list_entry, chunks_list);
+	return 0;
+}
+
+int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data,
+			    struct list_head *chunks_list,
+			    u32 num_of_data_chunks)
+{
+	struct mausb_payload_chunk *data_chunk = NULL;
+	struct mausb_payload_chunk *tmp = NULL;
+	u32 current_kvec = 0;
+
+	data->length = 0;
+	data->kvec = kcalloc(num_of_data_chunks, sizeof(struct kvec),
+			     GFP_KERNEL);
+	if (!data->kvec)
+		return -ENOMEM;
+
+	list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) {
+		data->kvec[current_kvec].iov_base =
+			data_chunk->kvec.iov_base;
+		data->kvec[current_kvec].iov_len =
+		    data_chunk->kvec.iov_len;
+		++data->kvec_num;
+		data->length += data_chunk->kvec.iov_len;
+		++current_kvec;
+	}
+	return 0;
+}
+
+void mausb_cleanup_chunks_list(struct list_head *chunks_list)
+{
+	struct mausb_payload_chunk *data_chunk = NULL;
+	struct mausb_payload_chunk *tmp = NULL;
+
+	list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) {
+		list_del(&data_chunk->list_entry);
+		kfree(data_chunk);
+	}
+}
+
+static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
+				     u32 byte_num,
+				     struct list_head *data_chunks_list,
+				     u32 *data_chunks_num)
+{
+	u32 rem_data		= 0;
+	u32 bytes_to_read	= 0;
+	struct mausb_payload_chunk *data_chunk = NULL;
+
+	(*data_chunks_num) = 0;
+
+	if (!data_chunks_list)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(data_chunks_list);
+	rem_data      = iterator->length - iterator->offset;
+	bytes_to_read = min(byte_num, rem_data);
+
+	if (bytes_to_read == 0)
+		return 0;
+
+	data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+
+	if (!data_chunk)
+		return -ENOMEM;
+
+	++(*data_chunks_num);
+
+	data_chunk->kvec.iov_base = (u8 *)(iterator->buffer) + iterator->offset;
+	data_chunk->kvec.iov_len = bytes_to_read;
+	iterator->offset += bytes_to_read;
+
+	list_add_tail(&data_chunk->list_entry, data_chunks_list);
+
+	return 0;
+}
+
+static int mausb_read_scatterlist_buffer(struct mausb_data_iter *iterator,
+					 u32 byte_num,
+					 struct list_head *data_chunks_list,
+					 u32 *data_chunks_num)
+{
+	u32 current_sg_read_num;
+	struct mausb_payload_chunk *data_chunk = NULL;
+
+	(*data_chunks_num) = 0;
+
+	if (!data_chunks_list)
+		return -EINVAL;
+
+	INIT_LIST_HEAD(data_chunks_list);
+
+	while (byte_num) {
+		if (iterator->sg_iter.consumed == iterator->sg_iter.length) {
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		}
+
+		data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL);
+		if (!data_chunk) {
+			sg_miter_stop(&iterator->sg_iter);
+			return -ENOMEM;
+		}
+
+		current_sg_read_num = min((size_t)byte_num,
+					  iterator->sg_iter.length -
+					  iterator->sg_iter.consumed);
+
+		data_chunk->kvec.iov_base = (u8 *)iterator->sg_iter.addr +
+				iterator->sg_iter.consumed;
+		data_chunk->kvec.iov_len  = current_sg_read_num;
+
+		++(*data_chunks_num);
+		list_add_tail(&data_chunk->list_entry, data_chunks_list);
+
+		byte_num -= current_sg_read_num;
+		iterator->sg_iter.consumed += current_sg_read_num;
+		data_chunk = NULL;
+	}
+
+	return 0;
+}
+
+static u32 mausb_write_virtual_buffer(struct mausb_data_iter *iterator,
+				      void *buffer, u32 size)
+{
+	u32 rem_space   = 0;
+	u32 write_count = 0;
+
+	if (!buffer || !size)
+		return write_count;
+
+	rem_space   = iterator->length - iterator->offset;
+	write_count = min(size, rem_space);
+
+	if (write_count > 0) {
+		void *location = shift_ptr(iterator->buffer, iterator->offset);
+
+		memcpy(location, buffer, write_count);
+		iterator->offset += write_count;
+	}
+
+	return write_count;
+}
+
+static u32 mausb_write_scatterlist_buffer(struct mausb_data_iter *iterator,
+					  void *buffer, u32 size)
+{
+	u32 current_sg_rem_space;
+	u32 count = 0;
+	u32 total_count = 0;
+	void *location = NULL;
+
+	if (!buffer || !size)
+		return count;
+
+	while (size) {
+		if (iterator->sg_iter.consumed >= iterator->sg_iter.length) {
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		}
+
+		current_sg_rem_space = (u32)(iterator->sg_iter.length -
+			iterator->sg_iter.consumed);
+
+		count = min(size, current_sg_rem_space);
+		total_count += count;
+
+		location = shift_ptr(iterator->sg_iter.addr,
+				     iterator->sg_iter.consumed);
+
+		memcpy(location, buffer, count);
+
+		buffer = shift_ptr(buffer, count);
+		size -= count;
+		iterator->sg_iter.consumed += count;
+	}
+
+	return total_count;
+}
+
+int mausb_data_iterator_read(struct mausb_data_iter *iterator,
+			     u32 byte_num,
+			     struct list_head *data_chunks_list,
+			     u32 *data_chunks_num)
+{
+	if (iterator->buffer)
+		return mausb_read_virtual_buffer(iterator, byte_num,
+						 data_chunks_list,
+						 data_chunks_num);
+	else
+		return mausb_read_scatterlist_buffer(iterator, byte_num,
+						     data_chunks_list,
+						     data_chunks_num);
+}
+
+u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer,
+			      u32 length)
+{
+	if (iterator->buffer)
+		return mausb_write_virtual_buffer(iterator, buffer, length);
+	else
+		return mausb_write_scatterlist_buffer(iterator, buffer, length);
+}
+
+static inline void mausb_seek_virtual_buffer(struct mausb_data_iter *iterator,
+					     u32 seek_delta)
+{
+	iterator->offset += min(seek_delta, iterator->length -
+					    iterator->offset);
+}
+
+static void mausb_seek_scatterlist_buffer(struct mausb_data_iter *iterator,
+					  u32 seek_delta)
+{
+	u32 rem_bytes_in_current_scatter;
+
+	while (seek_delta) {
+		rem_bytes_in_current_scatter = (u32)(iterator->sg_iter.length -
+						iterator->sg_iter.consumed);
+		if (rem_bytes_in_current_scatter <= seek_delta) {
+			iterator->sg_iter.consumed +=
+			    rem_bytes_in_current_scatter;
+			seek_delta -= rem_bytes_in_current_scatter;
+			if (!sg_miter_next(&iterator->sg_iter))
+				break;
+			iterator->sg_iter.consumed = 0;
+		} else {
+			iterator->sg_iter.consumed += seek_delta;
+			break;
+		}
+	}
+}
+
+void mausb_data_iterator_seek(struct mausb_data_iter *iterator,
+			      u32 seek_delta)
+{
+	if (iterator->buffer)
+		mausb_seek_virtual_buffer(iterator, seek_delta);
+	else
+		mausb_seek_scatterlist_buffer(iterator, seek_delta);
+}
+
+static void mausb_calculate_buffer_length(struct mausb_data_iter *iterator)
+{
+	/* Calculate buffer length */
+	if (iterator->buffer_len > 0) {
+		/* Transfer_buffer_length is populated */
+		iterator->length = iterator->buffer_len;
+	} else if (iterator->sg && iterator->num_sgs != 0) {
+		/* Transfer_buffer_length is not populated */
+		sg_miter_start(&iterator->sg_iter, iterator->sg,
+			       iterator->num_sgs, iterator->flags);
+		while (sg_miter_next(&iterator->sg_iter))
+			iterator->length += (u32)iterator->sg_iter.length;
+		sg_miter_stop(&iterator->sg_iter);
+	} else {
+		iterator->length = 0;
+	}
+}
+
+void mausb_init_data_iterator(struct mausb_data_iter *iterator, void *buffer,
+			      u32 buffer_len, struct scatterlist *sg,
+			      unsigned int num_sgs, bool direction)
+{
+	iterator->offset = 0;
+	iterator->buffer     = buffer;
+	iterator->buffer_len = buffer_len;
+	iterator->length     = 0;
+	iterator->sg	     = sg;
+	iterator->num_sgs    = num_sgs;
+	iterator->sg_started = false;
+
+	mausb_calculate_buffer_length(iterator);
+
+	if (!buffer && sg && num_sgs != 0) {
+		/* Scatterlist provided */
+		iterator->flags = direction ? SG_MITER_TO_SG : SG_MITER_FROM_SG;
+		sg_miter_start(&iterator->sg_iter, sg, num_sgs,
+			       iterator->flags);
+		iterator->sg_started = true;
+	}
+}
+
+void mausb_uninit_data_iterator(struct mausb_data_iter *iterator)
+{
+	iterator->offset     = 0;
+	iterator->length     = 0;
+	iterator->buffer     = NULL;
+	iterator->buffer_len = 0;
+
+	if (iterator->sg_started)
+		sg_miter_stop(&iterator->sg_iter);
+
+	iterator->sg_started = false;
+}
+
+void mausb_reset_data_iterator(struct mausb_data_iter *iterator)
+{
+	iterator->offset = 0;
+	if (iterator->sg_started) {
+		sg_miter_stop(&iterator->sg_iter);
+		iterator->sg_started = false;
+	}
+
+	if (!iterator->buffer && iterator->sg && iterator->num_sgs != 0) {
+		sg_miter_start(&iterator->sg_iter, iterator->sg,
+			       iterator->num_sgs, iterator->flags);
+		iterator->sg_started = true;
+	}
+}
+
+u32 mausb_data_iterator_length(struct mausb_data_iter *iterator)
+{
+	return iterator->length;
+}
diff --git a/drivers/usb/host/mausb/hpal.h b/drivers/usb/host/mausb/hpal.h
new file mode 100644
index 000000000000..f184bbc07783
--- /dev/null
+++ b/drivers/usb/host/mausb/hpal.h
@@ -0,0 +1,289 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_H__
+#define __MAUSB_HPAL_H__
+
+#include <linux/kref.h>
+#include <linux/suspend.h>
+#include <linux/usb.h>
+
+#include "ip_link.h"
+#include "mausb_address.h"
+#include "mausb_event.h"
+
+#define MAUSB_CONTROL_SETUP_SIZE	8
+#define MAUSB_BUSY_RETRIES_COUNT	3
+#define MAUSB_HEARTBEAT_TIMEOUT_MS	1000
+#define MAUSB_CLIENT_STOPPED_TIMEOUT_MS	3000
+
+#define MAUSB_MAX_RECEIVE_FAILURES	3
+#define MAUSB_MAX_MISSED_HEARTBEATS	3
+#define MAUSB_TRANSFER_RESERVED		0
+
+#define MAUSB_CHANNEL_MAP_LENGTH	4
+
+extern struct mss mss;
+extern struct mausb_hcd *mhcd;
+
+enum mausb_isoch_header_format_size {
+	MAUSB_ISOCH_SHORT_FORMAT_SIZE	 = 4,
+	MAUSB_ISOCH_STANDARD_FORMAT_SIZE = 8,
+	MAUSB_ISOCH_LONG_FORMAT_SIZE	 = 12
+};
+
+struct mausb_completion {
+	struct list_head   list_entry;
+	struct completion  *completion_event;
+	struct mausb_event *mausb_event;
+	u64		   event_id;
+};
+
+struct mausb_mss_rings_events {
+	atomic_t	  mausb_stop_reading_ring_events;
+	struct completion mausb_ring_has_events;
+};
+
+struct mss {
+	bool	   deinit_in_progress;
+	spinlock_t lock;	/* Protect mss structure */
+	u64	   ring_buffer_id;
+
+	struct completion empty;
+	struct completion client_stopped;
+	bool		  client_connected;
+	struct timer_list heartbeat_timer;
+	u8		  missed_heartbeats;
+
+	struct list_head  madev_list;
+	atomic_t	  num_of_transitions_to_sleep;
+	struct list_head  available_ring_buffers;
+
+	struct mausb_mss_rings_events	 rings_events;
+	struct mausb_events_notification events[MAUSB_MAX_NUM_OF_MA_DEVS];
+};
+
+struct mausb_device {
+	struct mausb_device_address dev_addr;
+	struct net		    *net_ns;
+	struct list_head	    list_entry;
+
+	struct mausb_ip_ctx *mgmt_channel;
+	struct mausb_ip_ctx *ctrl_channel;
+	struct mausb_ip_ctx *bulk_channel;
+	struct mausb_ip_ctx *isoch_channel;
+	struct mausb_ip_ctx *channel_map[MAUSB_CHANNEL_MAP_LENGTH];
+
+	struct work_struct work;
+	struct work_struct socket_disconnect_work;
+	struct work_struct hcd_disconnect_work;
+	struct work_struct madev_delete_work;
+	struct work_struct ping_work;
+	struct work_struct heartbeat_work;
+	struct workqueue_struct *workq;
+
+	struct kref refcount;
+	/* Set on port change event after cap resp */
+	u8 dev_type;
+	u8 dev_speed;
+	u8 lse;
+	u8 madev_addr;
+	u8 dev_connected;
+	u16 id;
+	u16 port_number;
+
+	u64		event_id;
+	spinlock_t	event_id_lock; /* Lock event ID increments */
+
+	struct list_head completion_events;
+	spinlock_t	 completion_events_lock; /* Lock completion events */
+
+	struct completion user_finished_event;
+	u16		  num_of_user_events;
+	u16		  num_of_completed_events;
+
+	spinlock_t	  num_of_user_events_lock; /* Lock user events count */
+
+	struct timer_list connection_timer;
+	u8		  receive_failures_num;
+	spinlock_t	  connection_timer_lock; /* Lock connection timer */
+
+	atomic_t	  unresponsive_client;
+
+	atomic_t	  num_of_usb_devices;
+};
+
+struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb);
+struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
+							    int status);
+struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr);
+
+static inline u64 mausb_event_id(struct mausb_device *dev)
+{
+	unsigned long flags;
+	u64 val;
+
+	spin_lock_irqsave(&dev->event_id_lock, flags);
+	val = ++(dev->event_id);
+	spin_unlock_irqrestore(&dev->event_id_lock, flags);
+
+	return val;
+}
+
+int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
+				 struct urb *request);
+int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
+		       u64 event_id);
+int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep);
+
+static inline void mausb_insert_event(struct mausb_device *dev,
+				      struct mausb_completion *event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_add_tail(&event->list_entry, &dev->completion_events);
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+}
+
+static inline void mausb_remove_event(struct mausb_device *dev,
+				      struct mausb_completion *event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->completion_events_lock, flags);
+	list_del(&event->list_entry);
+	spin_unlock_irqrestore(&dev->completion_events_lock, flags);
+}
+
+void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
+void mausb_complete_urb(struct mausb_event *event);
+void mausb_reset_connection_timer(struct mausb_device *dev);
+void mausb_reset_heartbeat_cnt(void);
+void mausb_release_event_resources(struct mausb_event  *event);
+void mausb_initialize_mss(void);
+void mausb_deinitialize_mss(void);
+int mausb_register_power_state_listener(void);
+void mausb_unregister_power_state_listener(void);
+
+void mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *
+				       std_desc,
+				       struct usb_endpoint_descriptor *
+				       usb_std_desc);
+void mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *
+					 ss_desc,
+					 struct usb_endpoint_descriptor *
+					 usb_std_desc,
+					 struct usb_ss_ep_comp_descriptor *
+					 usb_ss_desc);
+
+int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num,
+		    struct mausb_kvec_data_wrapper *data);
+
+int mausb_send_transfer_ack(struct mausb_device *dev,
+			    struct mausb_event *event);
+
+int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event);
+
+int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event);
+
+int mausb_add_data_chunk(void *buffer, u32 buffer_size,
+			 struct list_head *chunks_list);
+
+int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data,
+			    struct list_head *chunks_list,
+			    u32 num_of_data_chunks);
+
+void mausb_cleanup_chunks_list(struct list_head *chunks_list);
+
+static inline bool mausb_ctrl_transfer(struct ma_usb_hdr_common *hdr)
+{
+	return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL;
+}
+
+static inline bool mausb_isoch_transfer(struct ma_usb_hdr_common *hdr)
+{
+	return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH;
+}
+
+static inline bool mausb_ctrl_data_event(struct mausb_event *event)
+{
+	return event->data.transfer_type ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL;
+}
+
+static inline bool mausb_isoch_data_event(struct mausb_event *event)
+{
+	return event->data.transfer_type ==
+		MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH;
+}
+
+/* usb to mausb transfer type */
+static inline
+u8 mausb_transfer_type_from_usb(struct usb_endpoint_descriptor *epd)
+{
+	return (u8)usb_endpoint_type(epd) << 3;
+}
+
+static inline u8 mausb_transfer_type_from_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK;
+}
+
+static inline
+enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type)
+{
+	return transfer_type >> 3;
+}
+
+struct mausb_data_iter {
+	u32 length;
+
+	void *buffer;
+	u32  buffer_len;
+	u32  offset;
+
+	struct scatterlist	*sg;
+	struct sg_mapping_iter	sg_iter;
+	bool		sg_started;
+	unsigned int	num_sgs;
+	unsigned int	flags;
+};
+
+struct mausb_payload_chunk {
+	struct list_head list_entry;
+	struct kvec	 kvec;
+};
+
+int mausb_data_iterator_read(struct mausb_data_iter *iterator,
+			     u32 byte_num,
+			     struct list_head *data_chunks_list,
+			     u32 *data_chunks_num);
+
+u32 mausb_data_iterator_length(struct mausb_data_iter *iterator);
+u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer,
+			      u32 length);
+
+void mausb_init_data_iterator(struct mausb_data_iter *iterator,
+			      void *buffer, u32 buffer_len,
+			      struct scatterlist *sg, unsigned int num_sgs,
+			      bool direction);
+void mausb_reset_data_iterator(struct mausb_data_iter *iterator);
+void mausb_uninit_data_iterator(struct mausb_data_iter *iterator);
+void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta);
+
+static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
+						unsigned int elem_size)
+{
+	unsigned int num_of_pages = DIV_ROUND_UP(num_of_elems * elem_size,
+						 PAGE_SIZE);
+	unsigned int order = (unsigned int)ilog2(num_of_pages) +
+					(is_power_of_2(num_of_pages) ? 0 : 1);
+	return order;
+}
+
+#endif /* __MAUSB_HPAL_H__ */
diff --git a/drivers/usb/host/mausb/ma_usb.h b/drivers/usb/host/mausb/ma_usb.h
new file mode 100644
index 000000000000..65f6229c0dfe
--- /dev/null
+++ b/drivers/usb/host/mausb/ma_usb.h
@@ -0,0 +1,869 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MA_USB_H__
+#define __MAUSB_MA_USB_H__
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <types.h>
+#endif /* __KERNEL__ */
+
+#define MA_USB_SET_FIELD_(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, _v)
+#define MA_USB_GET_FIELD_(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, _v)
+#define MA_USB_SET_FIELD(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, \
+						    MA_USB_##_v)
+#define MA_USB_GET_FIELD(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, \
+						    MA_USB_##_v)
+
+#define MA_USB_MGMT_TOKEN_RESERVED  0
+#define MA_USB_MGMT_TOKEN_MIN       1
+#define MA_USB_MGMT_TOKEN_MAX       ((1 << 10) - 1)
+
+#define MA_USB_DATA_EPS_UNASSIGNED  0
+#define MA_USB_DATA_EPS_ACTIVE      1
+#define MA_USB_DATA_EPS_INACTIVE    2
+#define MA_USB_DATA_EPS_HALTED      3
+
+#define MA_USB_DATA_TFLAGS_ARQ      1
+#define MA_USB_DATA_TFLAGS_NEG      2
+#define MA_USB_DATA_TFLAGS_EOT      4
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL   0
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH  8
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_BULK   16
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_INTR   24
+
+#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK   0x18
+
+#define MA_USB_DATA_IFLAGS_MTD_VALID      1
+#define MA_USB_DATA_IFLAGS_HDR_FMT_SHORT  0
+#define MA_USB_DATA_IFLAGS_HDR_FMT_STD    2
+#define MA_USB_DATA_IFLAGS_HDR_FMT_LONG   4
+#define MA_USB_DATA_IFLAGS_IRS_FMT_STD    0
+#define MA_USB_DATA_IFLAGS_IRS_FMT_LONG   2
+#define MA_USB_DATA_IFLAGS_ASAP           8
+
+#define MA_USB_DATA_IFLAGS_FMT_MASK       0x6
+
+/* version */
+
+#define MA_USB_HDR_VERSION_1_0      0
+
+/* flags */
+
+#define MA_USB_HDR_FLAGS_HOST       1
+#define MA_USB_HDR_FLAGS_RETRY      2
+#define MA_USB_HDR_FLAGS_TIMESTAMP  4
+#define MA_USB_HDR_FLAGS_RESERVED   8
+#define MA_USB_HDR_FLAG(_f) MA_USB_HDR_FLAGS_##_f
+
+/* type and subtype */
+
+#define MA_USB_HDR_TYPE_TYPE_MASK     0xC0
+#define MA_USB_HDR_TYPE_SUBTYPE_MASK  0x3F
+
+#define MA_USB_HDR_TYPE_TYPE_MANAGEMENT 0
+#define MA_USB_HDR_TYPE_TYPE_CONTROL    1
+#define MA_USB_HDR_TYPE_TYPE_DATA       2
+
+/* Management subtypes */
+
+#define _MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2)
+#define _MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 1)
+
+#define MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \
+	_MA_USB_HDR_TYPE_MANAGEMENT_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \
+	_MA_USB_HDR_TYPE_MANAGEMENT_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+
+#define MA_USB_HDR_TYPE_SUBTYPE_CAP               0
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVHANDLE      1
+#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLE          2
+#define MA_USB_HDR_TYPE_SUBTYPE_EPACTIVATE        3
+#define MA_USB_HDR_TYPE_SUBTYPE_EPINACTIVATE      4
+#define MA_USB_HDR_TYPE_SUBTYPE_EPRESET           5
+#define MA_USB_HDR_TYPE_SUBTYPE_CLEARTRANSFERS    6
+#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLEDELETE    7
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVRESET          8
+#define MA_USB_HDR_TYPE_SUBTYPE_MODIFYEP0         9
+#define MA_USB_HDR_TYPE_SUBTYPE_SETUSBDEVADDR     10
+#define MA_USB_HDR_TYPE_SUBTYPE_UPDATEDEV         11
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVDISCONNECT  12
+#define MA_USB_HDR_TYPE_SUBTYPE_USBSUSPEND        13
+#define MA_USB_HDR_TYPE_SUBTYPE_USBRESUME         14
+#define MA_USB_HDR_TYPE_SUBTYPE_REMOTEWAKE        15
+#define MA_USB_HDR_TYPE_SUBTYPE_PING              16
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVDISCONNECT     17
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVINITDISCONNECT 18
+#define MA_USB_HDR_TYPE_SUBTYPE_SYNCH             19
+#define MA_USB_HDR_TYPE_SUBTYPE_CANCELTRANSFER    20
+#define MA_USB_HDR_TYPE_SUBTYPE_EPOPENSTREAM      21
+#define MA_USB_HDR_TYPE_SUBTYPE_EPCLOSESTREAM     22
+#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVRESET       23
+#define MA_USB_HDR_TYPE_SUBTYPE_DEVNOTIFICATION   24
+#define MA_USB_HDR_TYPE_SUBTYPE_EPSETKEEPALIVE    25
+#define MA_USB_HDR_TYPE_SUBTYPE_GETPORTBW         26
+#define MA_USB_HDR_TYPE_SUBTYPE_SLEEP             27
+#define MA_USB_HDR_TYPE_SUBTYPE_WAKE              28
+#define MA_USB_HDR_TYPE_SUBTYPE_VENDORSPECIFIC    31 /* Reserved */
+
+/* Data subtypes */
+
+#define _MA_USB_HDR_TYPE_DATA_REQ(_s) ({ \
+	typeof(_s) (s) = (_s); \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 \
+	+ ((s) > 0 ? 1 : 0)); })
+#define _MA_USB_HDR_TYPE_DATA_RESP(_s) ({ \
+	typeof(_s) (s) = (_s); \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 + 1 \
+	+ ((s) > 0 ? 1 : 0)); })
+#define _MA_USB_HDR_TYPE_DATA_ACK(_s) ( \
+	MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \
+	MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 2))
+
+#define MA_USB_HDR_TYPE_DATA_REQ(_s) \
+	_MA_USB_HDR_TYPE_DATA_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_DATA_RESP(_s) \
+	_MA_USB_HDR_TYPE_DATA_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+#define MA_USB_HDR_TYPE_DATA_ACK(_s) \
+	_MA_USB_HDR_TYPE_DATA_ACK(MA_USB_HDR_TYPE_SUBTYPE_##_s)
+
+#define MA_USB_HDR_TYPE_SUBTYPE_TRANSFER          0
+#define MA_USB_HDR_TYPE_SUBTYPE_ISOCHTRANSFER     1
+
+/* EP/Device Handle */
+
+#define MA_USB_DEVICE_HANDLE_RESERVED   0
+
+#define MA_USB_EP_HANDLE_D_MASK     0x0001
+#define MA_USB_EP_HANDLE_EP_N_MASK  0x001e
+#define MA_USB_EP_HANDLE_ADDR_MASK  0x0fe0
+#define MA_USB_EP_HANDLE_BUS_N_MASK 0xf000
+
+#define MA_USB_EP_HANDLE(_b, _a, _e, _d) ( \
+	MA_USB_SET_FIELD_(EP_HANDLE_BUS_N, _b)  | \
+	MA_USB_SET_FIELD_(EP_HANDLE_ADDR, _a)   | \
+	MA_USB_SET_FIELD_(EP_HANDLE_EP_N, _e)   | \
+	MA_USB_SET_FIELD_(EP_HANDLE_D, _d))
+
+#define MA_USB_EP_HANDLE_BUS_N_VIRTUAL  15
+#define MA_USB_EP_HANDLE_ADDR_DEFAULT   0
+#define MA_USB_EP_HANDLE_EP_N_DEFAULT   0
+#define MA_USB_EP_HANDLE_D_OUT          0	/* See USB2.0 9.3, Table 9-2 */
+#define MA_USB_EP_HANDLE_D_IN           1	/* See USB2.0 9.3, Table 9-2 */
+
+/* Status codes */
+
+#define MA_USB_HDR_STATUS_UNSUCCESSFUL                  -128
+#define MA_USB_HDR_STATUS_INVALID_MA_USB_SESSION_STATE  -127
+#define MA_USB_HDR_STATUS_INVALID_DEVICE_HANDLE         -126
+#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE             -125
+#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE_STATE       -124
+#define MA_USB_HDR_STATUS_INVALID_REQUEST               -123
+#define MA_USB_HDR_STATUS_MISSING_SEQUENCE_NUMBER       -122
+#define MA_USB_HDR_STATUS_TRANSFER_PENDING              -121
+#define MA_USB_HDR_STATUS_TRANSFER_EP_STALL             -120
+#define MA_USB_HDR_STATUS_TRANSFER_SIZE_ERROR           -119
+#define MA_USB_HDR_STATUS_TRANSFER_DATA_BUFFER_ERROR    -118
+#define MA_USB_HDR_STATUS_TRANSFER_BABBLE_DETECTED      -117
+#define MA_USB_HDR_STATUS_TRANSFER_TRANSACTION_ERROR    -116
+#define MA_USB_HDR_STATUS_TRANSFER_SHORT_TRANSFER       -115
+#define MA_USB_HDR_STATUS_TRANSFER_CANCELED             -114
+#define MA_USB_HDR_STATUS_INSUFICIENT_RESOURCES         -113
+#define MA_USB_HDR_STATUS_NOT_SUFFICIENT_BANDWIDTH      -112
+#define MA_USB_HDR_STATUS_INTERNAL_ERROR                -111
+#define MA_USB_HDR_STATUS_DATA_OVERRUN                  -110
+#define MA_USB_HDR_STATUS_DEVICE_NOT_ACCESSED           -109
+#define MA_USB_HDR_STATUS_BUFFER_OVERRUN                -108
+#define MA_USB_HDR_STATUS_BUSY                          -107
+#define MA_USB_HDR_STATUS_DROPPED_PACKET                -106
+#define MA_USB_HDR_STATUS_ISOCH_TIME_EXPIRED            -105
+#define MA_USB_HDR_STATUS_ISOCH_TIME_INVALID            -104
+#define MA_USB_HDR_STATUS_NO_USB_PING_RESPONSE          -103
+#define MA_USB_HDR_STATUS_NOT_SUPPORTED                 -102
+#define MA_USB_HDR_STATUS_REQUEST_DENIED                -101
+#define MA_USB_HDR_STATUS_MISSING_REQUEST_ID            -100
+#define MA_USB_HDR_STATUS_SUCCESS                       0	/* Reserved */
+#define MA_USB_HDR_STATUS_NO_ERROR MA_USB_HDR_STATUS_SUCCESS	/* Reserved */
+
+/* Speed values */
+
+#define MA_USB_SPEED_LOW_SPEED         0
+#define MA_USB_SPEED_FULL_SPEED        1
+#define MA_USB_SPEED_HIGH_SPEED        2
+#define MA_USB_SPEED_SUPER_SPEED       3
+#define MA_USB_SPEED_SUPER_SPEED_PLUS  4
+
+/* capreq extra hdr */
+
+#define MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH\
+	(sizeof(struct ma_usb_desc) +\
+	sizeof(struct ma_usb_capreq_desc_synchronization))
+#define MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH\
+	(sizeof(struct ma_usb_desc) +\
+	sizeof(struct ma_usb_capreq_desc_link_sleep))
+
+#define MA_USB_CAPREQ_LENGTH\
+	(sizeof(struct ma_usb_hdr_common) +\
+	sizeof(struct ma_usb_hdr_capreq) +\
+	MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH +\
+	MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH)
+
+/* capreq desc types */
+
+#define MA_USB_CAPREQ_DESC_TYPE_SYNCHRONIZATION 3
+#define MA_USB_CAPREQ_DESC_TYPE_LINK_SLEEP      5
+
+/* capresp descriptors */
+
+#define MA_USB_CAPRESP_DESC_TYPE_SPEED            0
+#define MA_USB_CAPRESP_DESC_TYPE_P_MANAGED_OUT    1
+#define MA_USB_CAPRESP_DESC_TYPE_ISOCHRONOUS      2
+#define MA_USB_CAPRESP_DESC_TYPE_SYNCHRONIZATION  3
+#define MA_USB_CAPRESP_DESC_TYPE_CONTAINER_ID     4
+#define MA_USB_CAPRESP_DESC_TYPE_LINK_SLEEP       5
+#define MA_USB_CAPRESP_DESC_TYPE_HUB_LATENCY      6
+
+/* Request ID and sequence number values */
+
+#define MA_USB_TRANSFER_RESERVED      0
+#define MA_USB_TRANSFER_REQID_MIN     0
+#define MA_USB_TRANSFER_REQID_MAX     ((1 <<  8) - 1)
+#define MA_USB_TRANSFER_SEQN_MIN      0
+#define MA_USB_TRANSFER_SEQN_MAX      ((1 << 24) - 2)
+#define MA_USB_TRANSFER_SEQN_INVALID  ((1 << 24) - 1)
+
+#define MA_USB_ISOCH_SFLAGS_FRAGMENT      0x1
+#define MA_USB_ISOCH_SFLAGS_LAST_FRAGMENT 0x2
+
+#define MAUSB_MAX_MGMT_SIZE 50
+
+#define MAUSB_TRANSFER_HDR_SIZE (u32)(sizeof(struct ma_usb_hdr_common) +\
+				      sizeof(struct ma_usb_hdr_transfer))
+
+#define MAUSB_ISOCH_TRANSFER_HDR_SIZE (u16)(sizeof(struct ma_usb_hdr_common) +\
+			sizeof(struct ma_usb_hdr_isochtransfer) +\
+			sizeof(struct ma_usb_hdr_isochtransfer_optional))
+
+#define MAX_ISOCH_ASAP_PACKET_SIZE (150000 /* Network MTU */ -\
+	MAUSB_ISOCH_TRANSFER_HDR_SIZE - 20 /* IP header size */ -\
+	8 /* UDP header size*/)
+
+#define shift_ptr(ptr, offset) ((u8 *)(ptr) + (offset))
+
+/* USB descriptor */
+struct ma_usb_desc {
+	u8 length;
+	u8 type;
+	u8 value[0];
+} __packed;
+
+struct ma_usb_ep_handle {
+	u16 d		:1,
+	    ep_n	:4,
+	    addr	:7,
+	    bus_n	:4;
+};
+
+struct ma_usb_hdr_mgmt {
+	u32 status	:8,
+	    token	:10,  /* requestor originator allocated */
+	    reserved	:14;
+} __aligned(4);
+
+struct ma_usb_hdr_ctrl {	/* used in all req/resp/conf operations */
+	s8 status;
+	u8 link_type;
+	union {
+		u8 tid;	/* ieee 802.11 */
+	} connection_id;
+} __aligned(4);
+
+struct ma_usb_hdr_data {
+	s8 status;
+	u8 eps		:2,
+	   t_flags	:6;
+	union {
+		u16 stream_id;
+		struct {
+			u16 headers	:12,
+			    i_flags	:4;
+		};
+	};
+} __aligned(4);
+
+struct ma_usb_hdr_common {
+	u8 version	:4,
+	   flags	:4;
+	u8  type;
+	u16 length;
+	union {
+		u16 dev;
+		u16 epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u8 dev_addr;
+	u8 ssid;
+	union {
+		s8 status;
+		struct ma_usb_hdr_mgmt mgmt;
+		struct ma_usb_hdr_ctrl ctrl;
+		struct ma_usb_hdr_data data;
+	};
+} __aligned(4);
+
+/* capreq extra hdr */
+
+struct ma_usb_hdr_capreq {
+	u32 out_mgmt_reqs	:12,
+	    reserved		:20;
+} __aligned(4);
+
+struct ma_usb_capreq_desc_synchronization {
+	u8 media_time_available	:1,
+	   reserved		:7;
+} __packed;
+
+struct ma_usb_capreq_desc_link_sleep {
+	u8 link_sleep_capable	:1,
+	   reserved		:7;
+} __packed;
+
+/* capresp extra hdr */
+
+struct ma_usb_hdr_capresp {
+	u16 endpoints;
+	u8 devices;
+	u8 streams		:5,
+	   dev_type		:3;
+	u32 descs		:8,
+	    descs_length	:24;
+	u16 out_transfer_reqs;
+	u16 out_mgmt_reqs	:12,
+	    reserved		:4;
+} __aligned(4);
+
+struct ma_usb_capresp_desc_speed {
+	u8 reserved1		:4,
+		speed		:4;
+	u8 reserved2		:4,
+	   lse			:2,	/* USB3.1 8.5.6.7, Table 8-22 */
+	   reserved3		:2;
+} __packed;
+
+struct ma_usb_capresp_desc_p_managed_out {
+	u8 elastic_buffer		:1,
+	   drop_notification		:1,
+	   reserved			:6;
+} __packed;
+
+struct ma_usb_capresp_desc_isochronous {
+	u8 payload_dword_aligned	:1,
+	   reserved			:7;
+} __packed;
+
+struct ma_usb_capresp_desc_synchronization {
+	u8 media_time_available	:1,
+	   time_stamp_required	:1,/* hubs need this set */
+	   reserved		:6;
+} __packed;
+
+struct ma_usb_capresp_desc_container_id {
+	u8 container_id[16];	/* UUID IETF RFC 4122 */
+} __packed;
+
+struct ma_usb_capresp_desc_link_sleep {
+	u8 link_sleep_capable	:1,
+	   reserved		:7;
+} __packed;
+
+struct ma_usb_capresp_desc_hub_latency {
+	u16 latency;		/* USB3.1 */
+} __packed;
+
+/* usbdevhandlereq extra hdr */
+struct ma_usb_hdr_usbdevhandlereq {
+	u32 route_string	:20,
+	    speed		:4,
+	    reserved1		:8;
+	u16 hub_dev_handle;
+	u16 reserved2;
+	u16 parent_hs_hub_dev_handle;
+	u16 parent_hs_hub_port		:4,
+	    mtt				:1,	/* USB2.0 11.14, 11.14.1.3 */
+	    lse				:2,	/* USB3.1 8.5.6.7, Table 8-22 */
+	    reserved3			:9;
+} __aligned(4);
+
+/* usbdevhandleresp extra hdr */
+struct ma_usb_hdr_usbdevhandleresp {
+	u16 dev_handle;
+	u16 reserved;
+} __aligned(4);
+
+/* ephandlereq extra hdr */
+struct ma_usb_hdr_ephandlereq {
+	u32 ep_descs		:5,
+	    ep_desc_size	:6,
+	    reserved		:21;
+} __aligned(4);
+
+/*
+ * Restricted USB2.0 ep desc limited to 6 bytes, isolating further changes.
+ * See USB2.0 9.6.6, Table 9-13
+ */
+struct usb_ep_desc {
+	u8 bLength;
+	/* USB2.0 9.4, Table 9-5 (5) usb/ch9.h: USB_DT_ENDPOINT */
+	u8 bDescriptorType;
+	u8  bEndpointAddress;
+	u8  bmAttributes;
+	__le16 wMaxPacketSize;
+	u8  bInterval;
+} __packed;
+
+/*
+ * Restricted USB3.1 ep comp desc isolating further changes in usb/ch9.h
+ * See USB3.1 9.6.7, Table 9-26
+ */
+struct usb_ss_ep_comp_desc {
+	u8 bLength;
+	/* USB3.1 9.4, Table 9-6 (48) usb/ch9.h: USB_DT_SS_ENDPOINT_COMP */
+	u8  bDescriptorType;
+	u8  bMaxBurst;
+	u8  bmAttributes;
+	__le16 wBytesPerInterval;
+} __packed;
+
+/*
+ * USB3.1 ss_plus_isoch_ep_comp_desc
+ * See USB3.1 9.6.8, Table 9-27
+ */
+struct usb_ss_plus_isoch_ep_comp_desc {
+	u8 bLength;
+	/* USB3.1 9.4, Table 9-6 (49) usb/ch9.h: not yet defined! */
+	u8 bDescriptorType;
+	u16 wReserved;
+	u32 dwBytesPerInterval;
+} __packed;
+
+struct ma_usb_ephandlereq_desc_std {
+	struct usb_ep_desc usb20;
+} __aligned(4);
+
+struct ma_usb_ephandlereq_desc_ss {
+	struct usb_ep_desc	   usb20;
+	struct usb_ss_ep_comp_desc usb31;
+} __aligned(4);
+
+struct ma_usb_ephandlereq_desc_ss_plus {
+	struct usb_ep_desc		      usb20;
+	struct usb_ss_ep_comp_desc	      usb31;
+	struct usb_ss_plus_isoch_ep_comp_desc usb31_isoch;
+} __aligned(4);
+
+struct ma_usb_dev_context {
+	struct usb_ep_desc usb;
+};
+
+/* ephandleresp extra hdr */
+struct ma_usb_hdr_ephandleresp {
+	u32 ep_descs :5,
+	    reserved :27;
+} __aligned(4);
+
+/* ephandleresp descriptor */
+struct ma_usb_ephandleresp_desc {
+	union {
+		struct ma_usb_ep_handle eph;
+		u16		epv;
+	} ep_handle;
+	u16 d		:1,	/* non-control or non-OUT */
+	    isoch	:1,
+	    l_managed	:1,	/* control or non-isoch OUT */
+	    invalid	:1,
+	    reserved1	:12;
+	u16 ccu;		/* control or non-isoch OUT */
+	u16 reserved2;
+	u32 buffer_size;	/* control or OUT */
+	u16 isoch_prog_delay;	/* in us. */
+	u16 isoch_resp_delay;	/* in us. */
+} __aligned(4);
+
+/* epactivatereq extra hdr */
+struct ma_usb_hdr_epactivatereq {
+	u32 ep_handles	:5,
+	    reserved	:27;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epactivateresp extra hdr */
+struct ma_usb_hdr_epactivateresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epinactivatereq extra hdr */
+struct ma_usb_hdr_epinactivatereq {
+	u32 ep_handles	:5,
+	    suspend	:1,
+	    reserved	:26;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epinactivateresp extra hdr */
+struct ma_usb_hdr_epinactivateresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16		epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* epresetreq extra hdr */
+struct ma_usb_hdr_epresetreq {
+	u32 ep_reset_blocks	:5,
+	    reserved		:27;
+} __aligned(4);
+
+/* epresetreq reset block */
+struct ma_usb_epresetreq_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 tsp		:1,
+	    reserved	:15;
+} __aligned(4);
+
+/* epresetresp extra hdr */
+struct ma_usb_hdr_epresetresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* cleartransfersreq extra hdr */
+struct ma_usb_hdr_cleartransfersreq {
+	u32 info_blocks	:8,
+	    reserved	:24;
+} __aligned(4);
+
+/* cleartransfersreq info block */
+struct ma_usb_cleartransfersreq_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id; /* ss stream eps only */
+	u32 start_req_id	:8,
+	    reserved		:24;
+} __aligned(4);
+
+/* cleartransfersresp extra hdr */
+struct ma_usb_hdr_cleartransfersresp {
+	u32 status_blocks	:8,
+	    reserved		:24;
+} __aligned(4);
+
+/* cleartransfersresp status block */
+struct ma_usb_cleartransfersresp_block {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;	/* ss stream eps only */
+	u32 cancel_success	:1,
+	    partial_delivery	:1,
+	    reserved		:30;
+	u32 last_req_id		:8,
+	    delivered_seq_n	:24;	/* OUT w/partial_delivery only */
+	u32 delivered_byte_offset;	/* OUT w/partial_delivery only */
+} __aligned(4);
+
+/* ephandledeletereq extra hdr */
+struct ma_usb_hdr_ephandledeletereq {
+	u32 ep_handles	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* ephandledeleteresp extra hdr */
+struct ma_usb_hdr_ephandledeleteresp {
+	u32 ep_errors	:5,
+	    reserved	:27;
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle[0];
+} __aligned(4);
+
+/* modifyep0req extra hdr */
+struct ma_usb_hdr_modifyep0req {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 max_packet_size;
+} __aligned(4);
+
+/*
+ * modifyep0resp extra hdr
+ * Only if req ep0 handle addr was 0 and req dev is in the addressed state
+ * or  if req ep0 handle addr != 0 and req dev is in default state
+ */
+struct ma_usb_hdr_modifyep0resp {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+
+	u16 reserved;
+} __aligned(4);
+
+/* setusbdevaddrreq extra hdr */
+struct ma_usb_hdr_setusbdevaddrreq {
+	u16 response_timeout;	/* in ms. */
+	u16 reserved;
+} __aligned(4);
+
+/* setusbdevaddrresp extra hdr */
+struct ma_usb_hdr_setusbdevaddrresp {
+	u32 addr	:7,
+	    reserved	:25;
+} __aligned(4);
+
+/* updatedevreq extra hdr */
+struct ma_usb_hdr_updatedevreq {
+	u16 max_exit_latency;	/* hubs only */
+	u8 hub		:1,
+	   ports	:4,
+	   mtt		:1,
+	   ttt		:2;
+	u8 integrated_hub_latency	:1,
+	   reserved			:7;
+} __aligned(4);
+
+/*
+ * USB2.0 dev desc, isolating further changes in usb/ch9.h
+ * See USB2.0 9.6.6, Table 9-13
+ */
+struct usb_dev_desc {
+	u8 bLength;
+	/*
+	 * USB2.0 9.4, Table 9-5 (1)
+	 * usb/ch9.h: USB_DT_DEVICE
+	 */
+	u8 bDescriptorType;
+	__le16 bcdUSB;
+	u8  bDeviceClass;
+	u8  bDeviceSubClass;
+	u8  bDeviceProtocol;
+	u8  bMaxPacketSize0;
+	__le16 idVendor;
+	__le16 idProduct;
+	__le16 bcdDevice;
+	u8  iManufacturer;
+	u8  iProduct;
+	u8  iSerialNumber;
+	u8  bNumConfigurations;
+} __packed;
+
+struct ma_usb_updatedevreq_desc {
+	struct usb_dev_desc usb20;
+} __aligned(4);
+
+/* remotewakereq extra hdr */
+struct ma_usb_hdr_remotewakereq {
+	u32 resumed  :1,
+		 reserved :31;
+} __aligned(4);
+
+/* synchreq/resp extra hdr */
+struct ma_usb_hdr_synch {
+	u32 mtd_valid		:1,	/* MA-USB1.0b 6.5.1.8, Table 66 */
+	    resp_required	:1,
+	    reserved		:30;
+	union {
+		u32 timestamp;		/* MA-USB1.0b 6.5.1.11 */
+		struct {
+			u32 delta		:13,
+			    bus_interval	:19;
+		};			/* MA-USB1.0b 6.6.1, Table 69 */
+	};
+	u32 mtd;			/* MA-USB1.0b 6.5.1.12 */
+} __aligned(4);
+
+/* canceltransferreq extra hdr */
+struct ma_usb_hdr_canceltransferreq {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;
+	u32 req_id	  :8,
+		 reserved :24;
+} __aligned(4);
+
+/* canceltransferresp extra hdr */
+struct ma_usb_hdr_canceltransferresp {
+	union {
+		u16			epv;
+		struct ma_usb_ep_handle eph;
+	} handle;
+	u16 stream_id;
+	u32 req_id		:8,
+	    cancel_status	:3,
+	    reserved1		:21;
+	u32 delivered_seq_n	:24,
+	    reserved2		:8;
+	u32 delivered_byte_offset;
+} __aligned(4);
+
+/* transferreq/resp/ack extra hdr */
+struct ma_usb_hdr_transfer {
+	u32 seq_n	:24,
+	    req_id	:8;
+	union {
+		u32 rem_size_credit;
+		/* ISOCH aliased fields added for convenience. */
+		struct {
+			u32 presentation_time	:20,
+			    segments		:12;
+		};
+	};
+} __aligned(4);
+
+/* isochtransferreq/resp extra hdr */
+struct ma_usb_hdr_isochtransfer {
+	u32 seq_n		:24,
+	    req_id		:8;
+	u32 presentation_time	:20,
+	    segments		:12;
+} __aligned(4);
+
+/* isochtransferreq/resp optional hdr */
+struct ma_usb_hdr_isochtransfer_optional {
+	union {
+		u32 timestamp;	/* MA-USB1.0b 6.5.1.11 */
+		struct {
+			u32 delta		:13,
+			    bus_interval	:19;
+		};		/* MA-USB1.0b 6.6.1, Table 69 */
+	};
+	u32 mtd;		/* MA-USB1.0b 6.5.1.12 */
+} __aligned(4);
+
+/* isochdatablock hdrs */
+
+struct ma_usb_hdr_isochdatablock_short {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+} __aligned(4);
+
+struct ma_usb_hdr_isochdatablock_std {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+	u16 segment_length;
+	u16 fragment_offset;
+} __aligned(4);
+
+struct ma_usb_hdr_isochdatablock_long {
+	u16 block_length;
+	u16 segment_number	:12,
+	    s_flags		:4;
+	u32 segment_length;
+	u32 fragment_offset;
+} __aligned(4);
+
+/* isochreadsizeblock hdrs */
+
+struct ma_usb_hdr_isochreadsizeblock_std {
+	u32 service_intervals		:12,
+	    max_segment_length		:20;
+} __aligned(4);
+
+struct ma_usb_hdr_isochreadsizeblock_long {
+	u32 service_intervals		:12,
+	    reserved			:20;
+	u32 max_segment_length;
+} __aligned(4);
+
+static inline int __mausb_set_field(int m, int v)
+{
+	return ((~((m) - 1) & (m)) * (v)) & (m);
+}
+
+static inline int __mausb_get_field(int m, int v)
+{
+	return ((v) & (m)) / (~((m) - 1) & (m));
+}
+
+static inline bool mausb_is_management_hdr_type(int hdr_type)
+{
+	return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type)
+			== MA_USB_HDR_TYPE_TYPE_MANAGEMENT;
+}
+
+static inline bool mausb_is_data_hdr_type(int hdr_type)
+{
+	return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type)
+			== MA_USB_HDR_TYPE_TYPE_DATA;
+}
+
+static inline bool mausb_is_management_resp_hdr_type(int hdr_resp_type)
+{
+	return mausb_is_management_hdr_type(hdr_resp_type) &&
+			(MA_USB_GET_FIELD_(HDR_TYPE_SUBTYPE, hdr_resp_type) & 1)
+			!= 0;
+}
+
+static inline
+struct ma_usb_hdr_transfer *
+mausb_get_data_transfer_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_transfer *)shift_ptr(hdr, sizeof(*hdr));
+}
+
+static inline
+struct ma_usb_hdr_isochtransfer *
+mausb_get_isochtransfer_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_isochtransfer *)shift_ptr(hdr, sizeof(*hdr));
+}
+
+static inline
+struct ma_usb_hdr_isochtransfer_optional *
+mausb_hdr_isochtransfer_optional_hdr(struct ma_usb_hdr_common *hdr)
+{
+	return (struct ma_usb_hdr_isochtransfer_optional *)
+			shift_ptr(hdr, sizeof(struct ma_usb_hdr_common) +
+				       sizeof(struct ma_usb_hdr_isochtransfer));
+}
+
+#endif	/* __MAUSB_MA_USB_H__ */
diff --git a/drivers/usb/host/mausb/mausb_address.h b/drivers/usb/host/mausb/mausb_address.h
new file mode 100644
index 000000000000..1a75482740ea
--- /dev/null
+++ b/drivers/usb/host/mausb/mausb_address.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_ADDRESS_H__
+#define __MAUSB_MAUSB_ADDRESS_H__
+
+#include <linux/inet.h>
+#include <linux/types.h>
+
+struct mausb_device_address {
+	u8 link_type;
+	struct {
+		char address[INET6_ADDRSTRLEN];
+		u8 number_of_ports;
+		struct {
+			u16 management;
+			u16 control;
+			u16 bulk;
+			u16 interrupt;
+			u16 isochronous;
+		} port;
+	} ip;
+};
+
+#endif /* __MAUSB_MAUSB_ADDRESS_H__ */
diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
index 485f241d2b4c..e5ccf4e9173b 100644
--- a/drivers/usb/host/mausb/mausb_core.c
+++ b/drivers/usb/host/mausb/mausb_core.c
@@ -5,6 +5,7 @@
 #include <linux/module.h>
 
 #include "hcd.h"
+#include "mausb_address.h"
 #include "utils.h"
 
 MODULE_LICENSE("GPL");
@@ -21,8 +22,16 @@ static int mausb_host_init(void)
 	if (status < 0)
 		goto cleanup_dev;
 
+	status = mausb_register_power_state_listener();
+	if (status < 0)
+		goto cleanup;
+
+	mausb_initialize_mss();
+
 	return 0;
 
+cleanup:
+	mausb_deinit_hcd();
 cleanup_dev:
 	mausb_host_dev_deregister();
 exit:
@@ -32,6 +41,8 @@ static int mausb_host_init(void)
 static void mausb_host_exit(void)
 {
 	dev_info(mausb_host_dev.this_device, "Module unloading started...");
+	mausb_unregister_power_state_listener();
+	mausb_deinitialize_mss();
 	mausb_deinit_hcd();
 	mausb_host_dev_deregister();
 }
diff --git a/drivers/usb/host/mausb/mausb_event.h b/drivers/usb/host/mausb/mausb_event.h
new file mode 100644
index 000000000000..a574f67d789d
--- /dev/null
+++ b/drivers/usb/host/mausb/mausb_event.h
@@ -0,0 +1,224 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_EVENT_H__
+#define __MAUSB_MAUSB_EVENT_H__
+
+#include "ma_usb.h"
+
+#define MAUSB_MAX_NUM_OF_MA_DEVS			15
+#define MAUSB_RING_BUFFER_SIZE				1024
+#define MAUSB_MAX_DATA_IN_REQ_SIZE			28
+
+#define MAUSB_EVENT_TYPE_DEV_RESET			1u
+#define MAUSB_EVENT_TYPE_USB_DEV_HANDLE			2u
+#define MAUSB_EVENT_TYPE_EP_HANDLE			3u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE		4u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE		5u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_RESET		6u
+#define MAUSB_EVENT_TYPE_EP_HANDLE_DELETE		7u
+#define MAUSB_EVENT_TYPE_MODIFY_EP0			8u
+#define MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS		9u
+#define MAUSB_EVENT_TYPE_UPDATE_DEV			10u
+#define MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT		11u
+#define MAUSB_EVENT_TYPE_PING				12u
+#define MAUSB_EVENT_TYPE_DEV_DISCONNECT			13u
+#define MAUSB_EVENT_TYPE_USB_DEV_RESET			14u
+#define MAUSB_EVENT_TYPE_CANCEL_TRANSFER		15u
+
+#define MAUSB_EVENT_TYPE_PORT_CHANGED			80u
+#define MAUSB_EVENT_TYPE_SEND_MGMT_MSG			81u
+#define MAUSB_EVENT_TYPE_SEND_DATA_MSG			82u
+#define MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG		83u
+#define MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG		84u
+#define MAUSB_EVENT_TYPE_URB_COMPLETE			85u
+#define MAUSB_EVENT_TYPE_SEND_ACK			86u
+#define MAUSB_EVENT_TYPE_ITERATOR_RESET			87u
+#define MAUSB_EVENT_TYPE_ITERATOR_SEEK			88u
+#define MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER		89u
+#define MAUSB_EVENT_TYPE_DELETE_MA_DEV			90u
+#define MAUSB_EVENT_TYPE_USER_FINISHED			91u
+#define MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES	92u
+#define MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED		93u
+#define MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT		94u
+
+#define MAUSB_EVENT_TYPE_NONE				255u
+
+#define MAUSB_DATA_MSG_DIRECTION_OUT			0
+#define MAUSB_DATA_MSG_DIRECTION_IN			1
+#define MAUSB_DATA_MSG_CONTROL MAUSB_DATA_MSG_DIRECTION_OUT
+
+struct mausb_devhandle {
+	u64 event_id;
+	u32 route_string;
+	u16 hub_dev_handle;
+	u16 parent_hs_hub_dev_handle;
+	u16 parent_hs_hub_port;
+	u16 mtt;
+	/* dev_handle assigned in user */
+	u16 dev_handle;
+	u8  device_speed;
+	u8  lse;
+};
+
+struct mausb_ephandle {
+	u64 event_id;
+	u16 device_handle;
+	u16 descriptor_size;
+	/* ep_handle assigned in user */
+	u16 ep_handle;
+	char	 descriptor[sizeof(struct ma_usb_ephandlereq_desc_ss)];
+};
+
+struct mausb_epactivate {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_epinactivate {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_epreset {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+	u8  tsp;
+};
+
+struct mausb_epdelete {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_updatedev {
+	u64 event_id;
+	u16 device_handle;
+	u16 max_exit_latency;
+	struct ma_usb_updatedevreq_desc update_descriptor;
+	u8  hub;
+	u8  number_of_ports;
+	u8  mtt;
+	u8  ttt;
+	u8  integrated_hub_latency;
+};
+
+struct mausb_usbdevreset {
+	u64 event_id;
+	u16 device_handle;
+};
+
+struct mausb_modifyep0 {
+	u64 event_id;
+	u16 device_handle;
+	u16 ep_handle;
+	__le16 max_packet_size;
+};
+
+struct mausb_setusbdevaddress {
+	u64 event_id;
+	u16 device_handle;
+	u16 response_timeout;
+};
+
+struct mausb_usbdevdisconnect {
+	u16 device_handle;
+};
+
+struct mausb_canceltransfer {
+	u64 urb;
+	u16 device_handle;
+	u16 ep_handle;
+};
+
+struct mausb_mgmt_hdr {
+	__aligned(4) char hdr[MAUSB_MAX_MGMT_SIZE];
+};
+
+struct mausb_mgmt_req_timedout {
+	u64 event_id;
+};
+
+struct mausb_delete_ma_dev {
+	u64 event_id;
+	u16 device_id;
+};
+
+/* TODO split mgmt_event to generic send mgmt req and specific requests */
+struct mausb_mgmt_event {
+	union {
+		struct mausb_devhandle		dev_handle;
+		struct mausb_ephandle		ep_handle;
+		struct mausb_epactivate		ep_activate;
+		struct mausb_epinactivate	ep_inactivate;
+		struct mausb_epreset		ep_reset;
+		struct mausb_epdelete		ep_delete;
+		struct mausb_modifyep0		modify_ep0;
+		struct mausb_setusbdevaddress	set_usb_dev_address;
+		struct mausb_updatedev		update_dev;
+		struct mausb_usbdevreset	usb_dev_reset;
+		struct mausb_usbdevdisconnect	usb_dev_disconnect;
+		struct mausb_canceltransfer	cancel_transfer;
+		struct mausb_mgmt_hdr		mgmt_hdr;
+		struct mausb_mgmt_req_timedout	mgmt_req_timedout;
+		struct mausb_delete_ma_dev	delete_ma_dev;
+	};
+};
+
+struct mausb_data_event {
+	uintptr_t urb;
+	uintptr_t recv_buf;
+	u32 iterator_seek_delta;
+	u32 transfer_size;
+	u32 rem_transfer_size;
+	u32 transfer_flags;
+	u32 isoch_seg_num;
+	u32 req_id;
+	u32 payload_size;
+	s32 status;
+
+	__aligned(4) char hdr[MAUSB_TRANSFER_HDR_SIZE];
+	__aligned(4) char hdr_ack[MAUSB_TRANSFER_HDR_SIZE];
+
+	u16 device_id;
+	u16 ep_handle;
+	u16 packet_size;
+	u8  setup_packet;
+	u8  direction;
+	u8  transfer_type;
+	u8  first_control_packet;
+	u8  transfer_eot;
+	u8  mausb_address;
+	u8  mausb_ssid;
+};
+
+struct mausb_port_changed_event {
+	u8 port;
+	u8 dev_type;
+	u8 dev_speed;
+	u8 lse;
+};
+
+struct mausb_event {
+	union {
+		struct mausb_mgmt_event		mgmt;
+		struct mausb_data_event		data;
+		struct mausb_port_changed_event port_changed;
+	};
+	s32 status;
+	u8 type;
+	u8 madev_addr;
+};
+
+struct mausb_events_notification {
+	u16 num_of_events;
+	u16 num_of_completed_events;
+	u8  madev_addr;
+};
+
+#endif /* __MAUSB_MAUSB_EVENT_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
                         ` (4 preceding siblings ...)
  2020-05-15 12:34       ` [PATCH v6 5/8] usb: mausb_host: Introduce PAL processing Vladimir Stankovic
@ 2020-05-15 12:35       ` Vladimir Stankovic
  2020-05-15 12:35       ` [PATCH v6 7/8] usb: mausb_host: MA-USB PAL events processing Vladimir Stankovic
                         ` (2 subsequent siblings)
  8 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:35 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemeneted connection between PAL and Link layers and set up
environment for exchanging PAL-to-PAL messages.

Within this patch, driver's sysfs parameters have been created
with intention to configure remote connection parameters.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/hpal.c       | 529 ++++++++++++++++++++++++++++
 drivers/usb/host/mausb/hpal.h       |   6 +
 drivers/usb/host/mausb/mausb_core.c | 140 ++++++++
 3 files changed, 675 insertions(+)

diff --git a/drivers/usb/host/mausb/hpal.c b/drivers/usb/host/mausb/hpal.c
index 5368c500a320..be600e8c30ec 100644
--- a/drivers/usb/host/mausb/hpal.c
+++ b/drivers/usb/host/mausb/hpal.c
@@ -9,10 +9,15 @@
 #include "hcd.h"
 #include "utils.h"
 
+#define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
+
 struct mss mss;
 
+static int mausb_start_connection_timer(struct mausb_device *dev);
 static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action,
 				void *data);
+static void mausb_signal_empty_mss(void);
+static void mausb_remove_madev_from_list(u8 madev_addr);
 static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work);
 static int mausb_start_heartbeat_timer(void);
 
@@ -173,6 +178,56 @@ static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx,
 	return true;
 }
 
+static void mausb_complete_urbs_from_tree(void)
+{
+	struct mausb_urb_ctx *urb_ctx = NULL;
+	struct urb	     *current_urb = NULL;
+	struct rb_node	     *current_node = NULL;
+	unsigned long flags;
+	int status = 0;
+	int ret;
+
+	dev_dbg(mausb_host_dev.this_device, "Completing all urbs from tree");
+
+	spin_lock_irqsave(&mhcd->lock, flags);
+
+	while ((current_node = rb_first(&mhcd->mausb_urbs))) {
+		urb_ctx = rb_entry(current_node, struct mausb_urb_ctx, rb_node);
+
+		current_urb = urb_ctx->urb;
+		mausb_delete_urb_ctx_from_tree(urb_ctx);
+		mausb_uninit_data_iterator(&urb_ctx->iterator);
+		kfree(urb_ctx);
+
+		ret = usb_hcd_check_unlink_urb(current_urb->hcpriv,
+					       current_urb, status);
+		if (ret == -EIDRM)
+			dev_warn(mausb_host_dev.this_device, "Urb=%p is already unlinked",
+				 current_urb);
+		else
+			usb_hcd_unlink_urb_from_ep(current_urb->hcpriv,
+						   current_urb);
+
+		spin_unlock_irqrestore(&mhcd->lock, flags);
+
+		/* Prepare urb for completion */
+		dev_dbg(mausb_host_dev.this_device, "Completing urb=%p",
+			current_urb);
+
+		current_urb->status	   = -EPROTO;
+		current_urb->actual_length = 0;
+		atomic_dec(&current_urb->use_count);
+		usb_hcd_giveback_urb(current_urb->hcpriv, current_urb,
+				     current_urb->status);
+
+		spin_lock_irqsave(&mhcd->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&mhcd->lock, flags);
+
+	dev_dbg(mausb_host_dev.this_device, "Completed all urbs from tree");
+}
+
 /*After this function call only valid thing to do with urb is to give it back*/
 struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb,
 							    int status)
@@ -280,6 +335,164 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev,
 		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+static void mausb_socket_disconnect_event(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						socket_disconnect_work);
+	struct mausb_event event;
+
+	dev_info(mausb_host_dev.this_device, "Disconnect madev_addr=%d",
+		 dev->madev_addr);
+
+	mausb_ip_disconnect(dev->ctrl_channel);
+	mausb_destroy_ip_ctx(dev->ctrl_channel);
+	dev->ctrl_channel = NULL;
+
+	mausb_ip_disconnect(dev->bulk_channel);
+	mausb_destroy_ip_ctx(dev->bulk_channel);
+	dev->bulk_channel = NULL;
+
+	mausb_ip_disconnect(dev->isoch_channel);
+	mausb_destroy_ip_ctx(dev->isoch_channel);
+	dev->isoch_channel = NULL;
+
+	if (dev->mgmt_channel) {
+		memset(&event, 0, sizeof(event));
+		event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
+		event.data.device_id = dev->id;
+
+		dev_info(mausb_host_dev.this_device, "Releasing MAUSB device ref");
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+	}
+
+	mausb_ip_disconnect(dev->mgmt_channel);
+	mausb_destroy_ip_ctx(dev->mgmt_channel);
+	dev->mgmt_channel = NULL;
+
+	memset(dev->channel_map, 0, sizeof(dev->channel_map));
+}
+
+static void mausb_disconnect_ma_dev(struct mausb_device *dev)
+{
+	dev_info(mausb_host_dev.this_device, "Disconnecting MAUSB device madev_addr=%d",
+		 dev->madev_addr);
+
+	if (!dev->dev_connected) {
+		dev_warn(mausb_host_dev.this_device, "MAUSB device is not connected");
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return;
+	}
+	mausb_hcd_disconnect(dev->port_number, dev->dev_type, dev->dev_speed);
+
+	if (dev->dev_type == USB30HUB)
+		mausb_hcd_disconnect(dev->port_number, USB20HUB, HIGH_SPEED);
+}
+
+static void mausb_hcd_disconnect_event(struct work_struct *work)
+{
+	struct mausb_device *ma_dev = container_of(work, struct mausb_device,
+						   hcd_disconnect_work);
+
+	mausb_disconnect_ma_dev(ma_dev);
+}
+
+static void mausb_delete_madev(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						madev_delete_work);
+	struct mausb_event	event;
+	struct completion	completion;
+	struct completion	*user_event;
+	struct mausb_completion mausb_completion;
+	long status;
+	unsigned long timeout = msecs_to_jiffies(MAUSB_DELETE_MADEV_TIMEOUT_MS);
+
+	dev_info(mausb_host_dev.this_device, "Deleting MAUSB device madev_addr=%d",
+		 dev->madev_addr);
+
+	del_timer_sync(&dev->connection_timer);
+
+	/* Client IS responsive */
+	if (!atomic_read(&dev->unresponsive_client)) {
+		memset(&event, 0, sizeof(event));
+		event.type = MAUSB_EVENT_TYPE_DELETE_MA_DEV;
+		event.mgmt.delete_ma_dev.device_id = dev->id;
+		event.mgmt.delete_ma_dev.event_id  = mausb_event_id(dev);
+
+		init_completion(&completion);
+		mausb_completion.completion_event = &completion;
+		mausb_completion.event_id = event.mgmt.delete_ma_dev.event_id;
+		mausb_completion.mausb_event = &event;
+
+		mausb_insert_event(dev, &mausb_completion);
+
+		dev_dbg(mausb_host_dev.this_device, "Deleting MAUSB device...");
+
+		status = wait_for_completion_interruptible_timeout(&completion,
+								   timeout);
+
+		dev_dbg(mausb_host_dev.this_device, "Deleting MAUSB device event finished with %ld",
+			status);
+
+		mausb_remove_event(dev, &mausb_completion);
+
+		user_event = &dev->user_finished_event;
+
+		status = wait_for_completion_interruptible_timeout(user_event,
+								   timeout);
+		dev_info(mausb_host_dev.this_device, "User event finished with %ld",
+			 status);
+	}
+
+	flush_workqueue(dev->workq);
+	destroy_workqueue(dev->workq);
+
+	mausb_clear_hcd_madev(dev->port_number);
+
+	mausb_remove_madev_from_list(dev->madev_addr);
+
+	put_net(dev->net_ns);
+
+	kfree(dev);
+	mausb_signal_empty_mss();
+
+	dev_dbg(mausb_host_dev.this_device, "MAUSB device deleted.");
+}
+
+static void mausb_ping_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						ping_work);
+
+	if (mausb_start_connection_timer(dev) < 0) {
+		dev_err(mausb_host_dev.this_device, "Session timeout - disconnnecting device madev_addr=%d",
+			dev->madev_addr);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return;
+	}
+}
+
+static void mausb_heartbeat_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						heartbeat_work);
+
+	dev_err(mausb_host_dev.this_device, "App is unresponsive - disconnect device");
+	atomic_set(&dev->unresponsive_client, 1);
+	mausb_complete_urbs_from_tree();
+	queue_work(dev->workq, &dev->socket_disconnect_work);
+	queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
+
+static void mausb_connection_timer_func(struct timer_list *timer)
+{
+	struct mausb_device *dev = container_of(timer, struct mausb_device,
+						connection_timer);
+
+	queue_work(dev->workq, &dev->ping_work);
+}
+
 static void mausb_heartbeat_timer_func(struct timer_list *timer)
 {
 	unsigned long flags = 0;
@@ -304,6 +517,99 @@ static void mausb_heartbeat_timer_func(struct timer_list *timer)
 	}
 }
 
+static struct mausb_device *
+mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
+		   int *status)
+{
+	struct mausb_device *dev;
+	unsigned long flags = 0;
+	char workq_name[16];
+	struct workqueue_struct *workq;
+
+	memset(workq_name, 0, sizeof(workq_name));
+	sprintf(workq_name, "%x", madev_address);
+	strcat(workq_name, "_madev_workq");
+
+	dev_vdbg(mausb_host_dev.this_device, "madev_workq_name = %s",
+		 workq_name);
+
+	workq = alloc_ordered_workqueue(workq_name, WQ_MEM_RECLAIM);
+	if (!workq) {
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	if (mss.deinit_in_progress) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		dev_alert(mausb_host_dev.this_device, "Device creating failed - mss deinit in progress");
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -ESHUTDOWN;
+		return NULL;
+	}
+
+	dev = mausb_get_dev_from_addr_unsafe(madev_address);
+	if (dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		dev_warn(mausb_host_dev.this_device, "MAUSB device already connected, madev_address=%x",
+			 madev_address);
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -EEXIST;
+		return NULL;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_ATOMIC);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		dev_alert(mausb_host_dev.this_device, "Could not allocate MAUSB device!");
+		flush_workqueue(workq);
+		destroy_workqueue(workq);
+		*status = -ENOMEM;
+		return NULL;
+	}
+
+	dev_dbg(mausb_host_dev.this_device, "Create MAUSB device.");
+
+	dev->workq = workq;
+
+	INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event);
+	INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event);
+	INIT_WORK(&dev->madev_delete_work, mausb_delete_madev);
+	INIT_WORK(&dev->ping_work, mausb_ping_work);
+	INIT_WORK(&dev->heartbeat_work, mausb_heartbeat_work);
+
+	kref_init(&dev->refcount);
+
+	dev->event_id = 0;
+	spin_lock_init(&dev->event_id_lock);
+
+	INIT_LIST_HEAD(&dev->completion_events);
+	spin_lock_init(&dev->completion_events_lock);
+	spin_lock_init(&dev->num_of_user_events_lock);
+	spin_lock_init(&dev->connection_timer_lock);
+
+	init_completion(&dev->user_finished_event);
+	atomic_set(&dev->unresponsive_client, 0);
+
+	timer_setup(&dev->connection_timer, mausb_connection_timer_func, 0);
+
+	dev->dev_addr = dev_addr;
+	dev->madev_addr = madev_address;
+	dev->net_ns = get_net(current->nsproxy->net_ns);
+
+	list_add_tail(&dev->list_entry, &mss.madev_list);
+
+	reinit_completion(&mss.empty);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return dev;
+}
+
 void mausb_release_ma_dev_async(struct kref *kref)
 {
 	struct mausb_device *dev = container_of(kref, struct mausb_device,
@@ -314,6 +620,46 @@ void mausb_release_ma_dev_async(struct kref *kref)
 	schedule_work(&dev->madev_delete_work);
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
+				  u8 madev_address)
+{
+	int error = 0;
+	struct mausb_device *dev;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(madev_address);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	if (dev) {
+		dev_warn(mausb_host_dev.this_device, "MAUSB device already connected, madev_address=%x",
+			 madev_address);
+		return -EEXIST;
+	}
+
+	dev = mausb_create_madev(dev_addr, madev_address, &error);
+	if (!dev)
+		return error;
+
+	dev_info(mausb_host_dev.this_device, "New MAUSB device created madev_addr=%d",
+		 madev_address);
+
+	error = mausb_init_ip_ctx(&dev->mgmt_channel, dev->net_ns,
+				  dev->dev_addr.ip.address,
+				  dev->dev_addr.ip.port.management, dev,
+				  mausb_ip_callback, MAUSB_MGMT_CHANNEL);
+	if (error) {
+		dev_err(mausb_host_dev.this_device, "Mgmt ip context init failed: error=%d",
+			error);
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return error;
+	}
+
+	mausb_ip_connect_async(dev->mgmt_channel);
+
+	return 0;
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 				  u16 num_of_completed)
 {
@@ -414,6 +760,26 @@ int mausb_signal_event(struct mausb_device *dev,
 	return -ETIMEDOUT;
 }
 
+static int mausb_start_connection_timer(struct mausb_device *dev)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&dev->connection_timer_lock, flags);
+
+	if (++dev->receive_failures_num > MAUSB_MAX_RECEIVE_FAILURES) {
+		dev_err(mausb_host_dev.this_device, "Missed more than %d ping responses",
+			MAUSB_MAX_RECEIVE_FAILURES);
+		spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000));
+
+	spin_unlock_irqrestore(&dev->connection_timer_lock, flags);
+
+	return 0;
+}
+
 void mausb_reset_connection_timer(struct mausb_device *dev)
 {
 	unsigned long flags = 0;
@@ -658,6 +1024,36 @@ struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr)
 	return NULL;
 }
 
+static void mausb_remove_madev_from_list(u8 madev_addr)
+{
+	unsigned long flags = 0;
+	struct mausb_device *ma_dev, *tmp = NULL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	list_for_each_entry_safe(ma_dev, tmp, &mss.madev_list, list_entry) {
+		if (ma_dev->madev_addr == madev_addr) {
+			list_del(&ma_dev->list_entry);
+			break;
+		}
+	}
+
+	if (list_empty(&mss.madev_list))
+		reinit_completion(&mss.rings_events.mausb_ring_has_events);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+static void mausb_signal_empty_mss(void)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (list_empty(&mss.madev_list))
+		complete(&mss.empty);
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
 static inline
 struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev,
 					    enum mausb_channel channel)
@@ -812,6 +1208,139 @@ void mausb_cleanup_chunks_list(struct list_head *chunks_list)
 	}
 }
 
+static void mausb_init_ip_ctx_helper(struct mausb_device *dev,
+				     struct mausb_ip_ctx **ip_ctx,
+				     u16 port,
+				     enum mausb_channel channel)
+{
+	int status = mausb_init_ip_ctx(ip_ctx, dev->net_ns,
+				       dev->dev_addr.ip.address, port, dev,
+				       mausb_ip_callback, channel);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Init ip context failed with error=%d",
+			status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	dev->channel_map[channel] = *ip_ctx;
+	mausb_ip_connect_async(*ip_ctx);
+}
+
+static void mausb_connect_callback(struct mausb_device *dev,
+				   enum mausb_channel channel, int status)
+{
+	struct mausb_device_address *dev_addr = &dev->dev_addr;
+
+	dev_info(mausb_host_dev.this_device, "Connect callback for channel=%d with status=%d",
+		      channel, status);
+
+	if (status < 0) {
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		return;
+	}
+
+	if (channel == MAUSB_MGMT_CHANNEL) {
+		if (dev_addr->ip.port.control == 0) {
+			dev->channel_map[MAUSB_CTRL_CHANNEL] =
+				dev->mgmt_channel;
+			channel = MAUSB_CTRL_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->ctrl_channel,
+						 dev_addr->ip.port.control,
+						 MAUSB_CTRL_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_CTRL_CHANNEL) {
+		if (dev_addr->ip.port.bulk == 0) {
+			dev->channel_map[MAUSB_BULK_CHANNEL] =
+				dev->channel_map[MAUSB_CTRL_CHANNEL];
+			channel = MAUSB_BULK_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->bulk_channel,
+						 dev_addr->ip.port.bulk,
+						 MAUSB_BULK_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_BULK_CHANNEL) {
+		if (dev_addr->ip.port.isochronous == 0) {
+			/* if there is no isoch port use tcp for it */
+			dev->channel_map[MAUSB_ISOCH_CHANNEL] =
+				dev->channel_map[MAUSB_BULK_CHANNEL];
+			channel = MAUSB_ISOCH_CHANNEL;
+		} else {
+			mausb_init_ip_ctx_helper(dev, &dev->isoch_channel,
+						 dev_addr->ip.port.isochronous,
+						 MAUSB_ISOCH_CHANNEL);
+			return;
+		}
+	}
+
+	if (channel == MAUSB_ISOCH_CHANNEL) {
+		dev->channel_map[MAUSB_INTR_CHANNEL] =
+				dev->channel_map[MAUSB_CTRL_CHANNEL];
+	}
+}
+
+static void mausb_handle_connect_event(struct mausb_device *dev,
+				       enum mausb_channel channel, int status,
+				       void *data)
+{
+	mausb_connect_callback(dev, channel, status);
+}
+
+static void mausb_handle_receive_event(struct mausb_device *dev,
+				       enum mausb_channel channel, int status,
+				       void *data)
+{
+	struct mausb_event event;
+
+	event.madev_addr = dev->madev_addr;
+
+	if (status <= 0) {
+		dev_err(mausb_host_dev.this_device, "Receive event error status=%d",
+			status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return;
+	}
+
+	mausb_reset_connection_timer(dev);
+}
+
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+		       enum mausb_link_action action, int status, void *data)
+{
+	struct mausb_device *dev = (struct mausb_device *)ctx;
+
+	switch (action) {
+	case MAUSB_LINK_CONNECT:
+		mausb_handle_connect_event(dev, channel, status, data);
+		break;
+	case MAUSB_LINK_SEND:
+		/*
+		 * Currently there is nothing to do, as send operation is
+		 * synchronous
+		 */
+		break;
+	case MAUSB_LINK_RECV:
+		mausb_handle_receive_event(dev, channel, status, data);
+		break;
+	case MAUSB_LINK_DISCONNECT:
+		/*
+		 * Currently there is nothing to do, as disconnect operation is
+		 * synchronous
+		 */
+		break;
+	default:
+		dev_warn(mausb_host_dev.this_device, "Unknown network action");
+	}
+}
+
 static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator,
 				     u32 byte_num,
 				     struct list_head *data_chunks_list,
diff --git a/drivers/usb/host/mausb/hpal.h b/drivers/usb/host/mausb/hpal.h
index f184bbc07783..a04ed120ba5e 100644
--- a/drivers/usb/host/mausb/hpal.h
+++ b/drivers/usb/host/mausb/hpal.h
@@ -131,6 +131,8 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
 	return val;
 }
 
+int mausb_initiate_dev_connection(struct mausb_device_address device_address,
+				  u8 madev_address);
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request);
 int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
@@ -158,6 +160,7 @@ static inline void mausb_remove_event(struct mausb_device *dev,
 }
 
 void mausb_release_ma_dev_async(struct kref *kref);
+void mausb_on_madev_connected(struct mausb_device *dev);
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status);
 void mausb_complete_urb(struct mausb_event *event);
 void mausb_reset_connection_timer(struct mausb_device *dev);
@@ -240,6 +243,9 @@ enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type)
 	return transfer_type >> 3;
 }
 
+void mausb_ip_callback(void *ctx, enum mausb_channel channel,
+		       enum mausb_link_action action, int status, void *data);
+
 struct mausb_data_iter {
 	u32 length;
 
diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
index e5ccf4e9173b..f394365700da 100644
--- a/drivers/usb/host/mausb/mausb_core.c
+++ b/drivers/usb/host/mausb/mausb_core.c
@@ -11,6 +11,146 @@
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("DisplayLink (UK) Ltd.");
 
+static struct mausb_device_address	device_address;
+static int				mausb_device_disconnect_param;
+static u16				madev_addr;
+static u8				mausb_client_connect_param;
+static u8				mausb_client_disconnect_param;
+
+static int mausb_client_connect(const char *value,
+				const struct kernel_param *kp)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (mss.client_connected) {
+		dev_err(mausb_host_dev.this_device, "MA-USB client is already connected");
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -EEXIST;
+	}
+	/* Save heartbeat client information */
+	mss.client_connected = true;
+	mss.missed_heartbeats = 0;
+	reinit_completion(&mss.client_stopped);
+	spin_unlock_irqrestore(&mss.lock, flags);
+	/* Start hearbeat timer */
+	mod_timer(&mss.heartbeat_timer,
+		  jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS));
+
+	return 0;
+}
+
+static int mausb_client_disconnect(const char *value,
+				   const struct kernel_param *kp)
+{
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	if (!mss.client_connected) {
+		dev_err(mausb_host_dev.this_device, "MA-USB client is not connected");
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return -ENODEV;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	/* Stop heartbeat timer */
+	del_timer_sync(&mss.heartbeat_timer);
+
+	/* Clear heartbeat client information */
+	spin_lock_irqsave(&mss.lock, flags);
+	mss.client_connected = false;
+	mss.missed_heartbeats = 0;
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		dev_vdbg(mausb_host_dev.this_device, "Enqueue heartbeat_work madev_addr=%x",
+			 dev->madev_addr);
+		queue_work(dev->workq, &dev->heartbeat_work);
+	}
+	complete(&mss.client_stopped);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+}
+
+static int mausb_device_connect(const char *value,
+				const struct kernel_param *kp)
+{
+	int status = 0;
+
+	if (strlen(value) <= INET6_ADDRSTRLEN) {
+		strcpy(device_address.ip.address, value);
+		dev_info(mausb_host_dev.this_device, "Processing '%s' address",
+			 device_address.ip.address);
+	} else {
+		dev_err(mausb_host_dev.this_device, "Invalid IP format");
+		return 0;
+	}
+	status = mausb_initiate_dev_connection(device_address, madev_addr);
+	memset(&device_address, 0, sizeof(device_address));
+
+	return status;
+}
+
+static int mausb_device_disconnect(const char *value,
+				   const struct kernel_param *kp)
+{
+	u8 dev_address = 0;
+	int status = 0;
+	unsigned long flags = 0;
+	struct mausb_device *dev = NULL;
+
+	status = kstrtou8(value, 0, &dev_address);
+	if (status < 0)
+		return -EINVAL;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	dev = mausb_get_dev_from_addr_unsafe(dev_address);
+	if (dev)
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+}
+
+static const struct kernel_param_ops mausb_device_connect_ops = {
+	.set = mausb_device_connect
+};
+
+static const struct kernel_param_ops mausb_device_disconnect_ops = {
+	.set = mausb_device_disconnect
+};
+
+static const struct kernel_param_ops mausb_client_connect_ops = {
+	.set = mausb_client_connect
+};
+
+static const struct kernel_param_ops mausb_client_disconnect_ops = {
+	.set = mausb_client_disconnect
+};
+
+module_param_named(mgmt, device_address.ip.port.management, ushort, 0664);
+MODULE_PARM_DESC(mgmt, "MA-USB management port");
+module_param_named(ctrl, device_address.ip.port.control, ushort, 0664);
+MODULE_PARM_DESC(ctrl, "MA-USB control port");
+module_param_named(bulk, device_address.ip.port.bulk, ushort, 0664);
+MODULE_PARM_DESC(bulk, "MA-USB bulk port");
+module_param_named(isoch, device_address.ip.port.isochronous, ushort, 0664);
+MODULE_PARM_DESC(isoch, "MA-USB isochronous port");
+module_param_named(madev_addr, madev_addr, ushort, 0664);
+MODULE_PARM_DESC(madev_addr, "MA-USB device address");
+
+module_param_cb(client_connect, &mausb_client_connect_ops,
+		&mausb_client_connect_param, 0664);
+module_param_cb(client_disconnect, &mausb_client_disconnect_ops,
+		&mausb_client_disconnect_param, 0664);
+module_param_cb(ip, &mausb_device_connect_ops,
+		device_address.ip.address, 0664);
+module_param_cb(disconnect, &mausb_device_disconnect_ops,
+		&mausb_device_disconnect_param, 0664);
+
 static int mausb_host_init(void)
 {
 	int status = mausb_host_dev_register();
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 7/8] usb: mausb_host: MA-USB PAL events processing
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
                         ` (5 preceding siblings ...)
  2020-05-15 12:35       ` [PATCH v6 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication Vladimir Stankovic
@ 2020-05-15 12:35       ` Vladimir Stankovic
  2020-05-15 12:35       ` [PATCH v6 8/8] usb: mausb_host: Process MA-USB data packets Vladimir Stankovic
  2020-05-15 13:08       ` [PATCH v6 0/8] Add MA USB Host driver Greg KH
  8 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:35 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Implemented MA-USB management messages processing and communication
with user-space driver via mapped memory.

MA USB PAL events are being enqueued into ring buffer from which
data is being send/received to/from user-space.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/Makefile              |   1 +
 drivers/usb/host/mausb/hcd.c                 | 245 +++++++-
 drivers/usb/host/mausb/hpal.c                | 456 +++++++++++++-
 drivers/usb/host/mausb/hpal.h                |  47 +-
 drivers/usb/host/mausb/hpal_events.c         | 614 +++++++++++++++++++
 drivers/usb/host/mausb/hpal_events.h         |  85 +++
 drivers/usb/host/mausb/mausb_driver_status.h |  17 +
 drivers/usb/host/mausb/utils.c               | 270 ++++++++
 drivers/usb/host/mausb/utils.h               |   7 +-
 9 files changed, 1726 insertions(+), 16 deletions(-)
 create mode 100644 drivers/usb/host/mausb/hpal_events.c
 create mode 100644 drivers/usb/host/mausb/hpal_events.h
 create mode 100644 drivers/usb/host/mausb/mausb_driver_status.h

diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
index 0f9b9be38907..b0423f5d6a14 100644
--- a/drivers/usb/host/mausb/Makefile
+++ b/drivers/usb/host/mausb/Makefile
@@ -11,3 +11,4 @@ mausb_host-y += utils.o
 mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
+mausb_host-y += hpal_events.o
diff --git a/drivers/usb/host/mausb/hcd.c b/drivers/usb/host/mausb/hcd.c
index 26d582ee06e9..b6160c894d25 100644
--- a/drivers/usb/host/mausb/hcd.c
+++ b/drivers/usb/host/mausb/hcd.c
@@ -4,6 +4,9 @@
  */
 #include "hcd.h"
 
+#include <linux/miscdevice.h>
+
+#include "hpal_events.h"
 #include "utils.h"
 
 static int mausb_bus_probe(struct device *dev);
@@ -15,9 +18,9 @@ static unsigned int major;
 static unsigned int minor = 1;
 static dev_t devt;
 static struct device *device;
-
 struct mausb_hcd	*mhcd;
 static struct class	*mausb_class;
+
 static struct bus_type	mausb_bus_type = {
 	.name	= DEVICE_NAME,
 	.match	= mausb_bus_match,
@@ -997,6 +1000,17 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 	}
 
 	if (ep_ctx) {
+		status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
+							  ep_ctx->ep_handle);
+
+		dev_info(mausb_host_dev.this_device, "epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d",
+			 ep_ctx->ep_handle, dev_handle, status);
+
+		status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
+						      ep_ctx->ep_handle);
+		if (status < 0)
+			dev_warn(mausb_host_dev.this_device, "ep_handle_del request failed for ep0: ep_handle=%#x, dev_handle=%#x",
+				 ep_ctx->ep_handle, dev_handle);
 		dev->ep0.hcpriv = NULL;
 		kfree(ep_ctx);
 	} else {
@@ -1004,6 +1018,14 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 			 dev_handle);
 	}
 
+	if (dev_handle != DEV_HANDLE_NOT_ASSIGNED) {
+		status = mausb_usbdevdisconnect_event_to_user(ma_dev,
+							      dev_handle);
+		if (status < 0)
+			dev_warn(mausb_host_dev.this_device, "usb_dev_disconnect req failed for dev_handle=%#x",
+				 dev_handle);
+	}
+
 free_dev:
 	if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
 		dev_info(mausb_host_dev.this_device, "All usb devices destroyed - proceed with disconnecting");
@@ -1019,6 +1041,22 @@ static void mausb_free_dev(struct usb_hcd *hcd, struct usb_device *dev)
 		mausb_clear_hcd_madev(port_number);
 }
 
+static int mausb_device_assign_address(struct mausb_device *dev,
+				       struct mausb_usb_device_ctx *usb_dev_ctx)
+{
+	int status =
+		mausb_setusbdevaddress_event_to_user(dev,
+						     usb_dev_ctx->dev_handle,
+						     RESPONSE_TIMEOUT);
+
+	dev_info(mausb_host_dev.this_device, "dev_handle=%#x, status=%d",
+		 usb_dev_ctx->dev_handle, status);
+
+	usb_dev_ctx->addressed = (status == 0);
+
+	return status;
+}
+
 static struct mausb_usb_device_ctx *
 mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
 		       struct mausb_device *ma_dev, u16 port_number,
@@ -1101,6 +1139,12 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 			return status;
 	}
 
+	if (!usb_device_ctx->addressed) {
+		status = mausb_device_assign_address(ma_dev, usb_device_ctx);
+		if (status < 0)
+			return status;
+	}
+
 	endpoint_ctx = dev->ep0.hcpriv;
 	if (!endpoint_ctx) {
 		dev_err(&dev->dev, "endpoint_ctx is NULL: dev_handle=%#x",
@@ -1108,7 +1152,18 @@ static int mausb_address_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -EINVAL;
 	}
 
-	return 0;
+	if (dev->speed < USB_SPEED_SUPER && endpoint_ctx->ep_handle != 0)
+		return 0;
+
+	status = mausb_modifyep0_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       &endpoint_ctx->ep_handle,
+					       dev->ep0.desc.wMaxPacketSize);
+
+	dev_info(&dev->dev, "modify ep0 response, ep_handle=%#x, dev_handle=%#x, status=%d",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, status);
+
+	return status;
 }
 
 static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
@@ -1160,10 +1215,32 @@ static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
 						    &endpoint->desc,
 						    &endpoint->ss_ep_comp);
+		status = mausb_ephandle_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      sizeof(descriptor_ss),
+						      &descriptor_ss,
+						      &endpoint_ctx->ep_handle);
+
 	} else {
 		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+		status = mausb_ephandle_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      sizeof(descriptor),
+						      &descriptor,
+						      &endpoint_ctx->ep_handle);
 	}
 
+	if (status < 0) {
+		dev_err(&dev->dev, "ep_handle_request failed dev_handle=%#x",
+			usb_dev_ctx->dev_handle);
+		endpoint->hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return status;
+	}
+
+	dev_info(&dev->dev, "Endpoint added ep_handle=%#x, dev_handle=%#x",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
 	return 0;
 }
 
@@ -1172,6 +1249,7 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 {
 	u8	port_number;
 	int	status;
+	int	retries	    = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_dev_ctx;
@@ -1206,9 +1284,48 @@ static int mausb_drop_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
 		return -ENODEV;
 	}
 
+	dev_info(&dev->dev, "Start dropping ep_handle=%#x, dev_handle=%#x",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
+	if (atomic_read(&ma_dev->unresponsive_client)) {
+		dev_err(&dev->dev, "Client is not responsive anymore - drop endpoint immediately");
+		endpoint->hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return -ESHUTDOWN;
+	}
+
+	status = mausb_epinactivate_event_to_user(ma_dev,
+						  usb_dev_ctx->dev_handle,
+						  endpoint_ctx->ep_handle);
+
+	dev_info(&dev->dev, "epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, status);
+
+	while (true) {
+		status = mausb_epdelete_event_to_user(ma_dev,
+						      usb_dev_ctx->dev_handle,
+						      endpoint_ctx->ep_handle);
+
+		dev_info(&dev->dev, "ep_handle_delete_request, ep_handle=%#x, dev_handle=%#x, status=%d",
+			 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
+			 status);
+
+		if (status == -EBUSY) {
+			if (++retries < MAUSB_BUSY_RETRIES_COUNT)
+				usleep_range(10000, 10001);
+			else
+				return -EBUSY;
+		} else {
+			break;
+		}
+	}
+
+	dev_info(&dev->dev, "Endpoint dropped ep_handle=%#x, dev_handle=%#x",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
+
 	endpoint->hcpriv = NULL;
 	kfree(endpoint_ctx);
-	return 0;
+	return status;
 }
 
 static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
@@ -1261,6 +1378,22 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
 		return -EINVAL;
 	}
 
+	status = mausb_usbdevhandle_event_to_user(ma_dev, (u8)dev_speed,
+						  dev->route, hub_dev_handle,
+						  parent_hs_hub_dev_handle,
+						  parent_hs_hub_port, 0,
+						  ma_dev->lse,
+						  &usb_device_ctx->dev_handle);
+
+	dev_dbg(mausb_host_dev.this_device, "mausb_usbdevhandle_event status=%d",
+		status);
+
+	if (status < 0)
+		return status;
+
+	dev_vdbg(mausb_host_dev.this_device, "dev_handle=%#x",
+		 usb_device_ctx->dev_handle);
+
 	endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
 	if (!endpoint_ctx)
 		return -ENOMEM;
@@ -1272,6 +1405,21 @@ static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
 
 	mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
 
+	status = mausb_ephandle_event_to_user(ma_dev,
+					      usb_device_ctx->dev_handle,
+					      sizeof(descriptor),
+					      &descriptor,
+					      &endpoint_ctx->ep_handle);
+
+	dev_info(mausb_host_dev.this_device, "mausb_ephandle_event ep_handle=%#x, dev_handle=%#x, status=%d",
+		 endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, status);
+
+	if (status < 0) {
+		dev->ep0.hcpriv = NULL;
+		kfree(endpoint_ctx);
+		return status;
+	}
+
 	return 0;
 }
 
@@ -1315,6 +1463,12 @@ static int mausb_enable_device(struct usb_hcd *hcd, struct usb_device *dev)
 	if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
 		return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
 						      usb_device_ctx);
+
+	if (!usb_device_ctx->addressed)
+		return mausb_device_assign_address(ma_dev, usb_device_ctx);
+
+	dev_info(mausb_host_dev.this_device, "Device assigned and addressed dev_handle=%#x",
+		 usb_device_ctx->dev_handle);
 	return 0;
 }
 
@@ -1360,7 +1514,15 @@ static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev)
 		return -ENODEV;
 	}
 
-	return 0;
+	status = mausb_updatedev_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       0, 0, 0, 0, 0, 0,
+					       &dev->descriptor);
+
+	dev_info(mausb_host_dev.this_device, "Finished dev_handle=%#x, status=%d",
+		 usb_device_ctx->dev_handle, status);
+
+	return status;
 }
 
 static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
@@ -1370,8 +1532,10 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 	u8	port_number;
 	unsigned long flags;
 	u16 max_exit_latency = 0;
+	u8  number_of_ports = (u8)dev->maxchild;
 	u8  mtt = 0;
 	u8  ttt = 0;
+	u8  integrated_hub_latency = 0;
 	struct hub_ctx		    *hub = (struct hub_ctx *)hcd->hcd_priv;
 	struct mausb_device	    *ma_dev;
 	struct mausb_usb_device_ctx *usb_device_ctx;
@@ -1411,7 +1575,17 @@ static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
 	else if (dev->usb3_lpm_u2_enabled)
 		max_exit_latency = (u16)dev->u2_params.mel;
 
-	return 0;
+	status = mausb_updatedev_event_to_user(ma_dev,
+					       usb_device_ctx->dev_handle,
+					       max_exit_latency, 1,
+					       number_of_ports, mtt, ttt,
+					       integrated_hub_latency,
+					       &dev->descriptor);
+
+	dev_info(mausb_host_dev.this_device, "Finished dev_handle=%#x, status=%d",
+		 usb_device_ctx->dev_handle, status);
+
+	return status;
 }
 
 static void mausb_endpoint_reset(struct usb_hcd *hcd,
@@ -1430,6 +1604,8 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	struct mausb_usb_device_ctx *usb_device_ctx;
 	struct usb_device	    *dev;
 	struct mausb_endpoint_ctx   *ep_ctx;
+	struct ma_usb_ephandlereq_desc_ss  descriptor_ss;
+	struct ma_usb_ephandlereq_desc_std descriptor;
 
 	ep_ctx = endpoint->hcpriv;
 	if (!ep_ctx)
@@ -1462,6 +1638,15 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 	is_out = usb_endpoint_dir_out(&endpoint->desc);
 	tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
 
+	status = mausb_epreset_event_to_user(ma_dev, dev_handle,
+					     ep_ctx->ep_handle, tsp);
+
+	dev_info(&dev->dev, "ep_reset status=%d, ep_handle=%#x, dev_handle=%#x",
+		 status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
 	if (status != EUCLEAN) {
 		if (!tsp) {
 			usb_settoggle(dev, epnum, is_out, 0U);
@@ -1469,12 +1654,52 @@ static void mausb_endpoint_reset(struct usb_hcd *hcd,
 				usb_settoggle(dev, epnum, !is_out, 0U);
 		}
 
+		status = mausb_epactivate_event_to_user(ma_dev, dev_handle,
+							ep_ctx->ep_handle);
+
+		dev_err(&dev->dev, "ep_activate failed, status=%d, ep_handle=%#x, dev_handle=%#x",
+			status, ep_ctx->ep_handle, dev_handle);
+
 		return;
 	}
 
 	if (tsp)
 		return;
 
+	status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
+						  ep_ctx->ep_handle);
+
+	dev_info(&dev->dev, "ep_inactivate status=%d, ep_handle=%#x, dev_handle=%#x",
+		 status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
+	status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
+					      ep_ctx->ep_handle);
+
+	dev_info(&dev->dev, "ep_handle_delete status=%d, ep_handle=%#x, dev_handle=%#x",
+		 status, ep_ctx->ep_handle, dev_handle);
+
+	if (status < 0)
+		return;
+
+	if (dev->speed >= USB_SPEED_SUPER) {
+		mausb_init_superspeed_ep_descriptor(&descriptor_ss,
+						    &endpoint->desc,
+						    &endpoint->ss_ep_comp);
+		status = mausb_ephandle_event_to_user(ma_dev, dev_handle,
+						      sizeof(descriptor_ss),
+						      &descriptor_ss,
+						      &ep_ctx->ep_handle);
+	} else {
+		mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
+		status = mausb_ephandle_event_to_user(ma_dev, dev_handle,
+						      sizeof(descriptor),
+						      &descriptor,
+						      &ep_ctx->ep_handle);
+	}
+
 	dev_info(&dev->dev, "ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x",
 		 status, ep_ctx->ep_handle, dev_handle);
 }
@@ -1516,7 +1741,15 @@ static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev)
 
 	dev_handle = usb_device_ctx->dev_handle;
 
-	return 0;
+	status = mausb_usbdevreset_event_to_user(ma_dev, dev_handle);
+
+	dev_info(mausb_host_dev.this_device, "usb_dev_reset dev_handle=%#x, status=%d",
+		 dev_handle, status);
+
+	if (status == 0)
+		usb_device_ctx->addressed = false;
+
+	return status;
 }
 
 void mausb_clear_hcd_madev(u16 port_number)
diff --git a/drivers/usb/host/mausb/hpal.c b/drivers/usb/host/mausb/hpal.c
index be600e8c30ec..19d74ffb1610 100644
--- a/drivers/usb/host/mausb/hpal.c
+++ b/drivers/usb/host/mausb/hpal.c
@@ -7,6 +7,7 @@
 #include <linux/circ_buf.h>
 
 #include "hcd.h"
+#include "hpal_events.h"
 #include "utils.h"
 
 #define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
@@ -275,6 +276,31 @@ void mausb_release_event_resources(struct mausb_event *event)
 	kfree(receive_buffer);
 }
 
+static void mausb_iterator_reset(struct mausb_device *dev,
+				 struct mausb_event *event)
+{
+	struct urb	     *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+
+	urb_ctx = mausb_find_urb_in_tree(urb);
+
+	if (urb_ctx)
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+}
+
+static void mausb_iterator_seek(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	struct urb	     *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+
+	urb_ctx = mausb_find_urb_in_tree(urb);
+
+	if (urb_ctx)
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 event->data.iterator_seek_delta);
+}
+
 void mausb_complete_urb(struct mausb_event *event)
 {
 	struct urb *urb = (struct urb *)event->data.urb;
@@ -288,6 +314,48 @@ void mausb_complete_urb(struct mausb_event *event)
 			       event->status);
 }
 
+static void mausb_delete_ma_dev(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	mausb_signal_event(dev, event, event->mgmt.delete_ma_dev.event_id);
+}
+
+static void mausb_process_user_finished(struct mausb_device *dev,
+					struct mausb_event *event)
+{
+	complete(&dev->user_finished_event);
+}
+
+static int mausb_send_mgmt_msg(struct mausb_device *dev,
+			       struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper wrapper;
+	struct kvec kvec;
+	struct ma_usb_hdr_common *hdr;
+	int status;
+
+	hdr = (struct ma_usb_hdr_common *)event->mgmt.mgmt_hdr.hdr;
+
+	dev_info(mausb_host_dev.this_device, "Sending event=%d, type=%d",
+		 event->type, hdr->type);
+
+	kvec.iov_base	 = hdr;
+	kvec.iov_len	 = hdr->length;
+	wrapper.kvec	 = &kvec;
+	wrapper.kvec_num = 1;
+	wrapper.length	 = hdr->length;
+
+	status = mausb_ip_send(dev->mgmt_channel, &wrapper);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Send failed. Disconnecting... status=%d",
+			status);
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+	}
+
+	return status;
+}
+
 static int mausb_get_first_free_port_number(u16 *port_number)
 {
 	(*port_number) = 0;
@@ -335,11 +403,144 @@ static inline void mausb_port_has_changed_event(struct mausb_device *dev,
 		mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev);
 }
 
+static void mausb_complete_timeout_event(struct mausb_device *dev,
+					 struct mausb_event *event)
+{
+	dev_vdbg(mausb_host_dev.this_device, "Event type=%d, event_id=%llu",
+		 event->type, event->mgmt.mgmt_req_timedout.event_id);
+	mausb_signal_event(dev, event, event->mgmt.mgmt_req_timedout.event_id);
+}
+
+static void mausb_process_event(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	dev_vdbg(mausb_host_dev.this_device, "Process event of type=%d",
+		 event->type);
+
+	switch (event->type) {
+	case MAUSB_EVENT_TYPE_USB_DEV_HANDLE:
+		mausb_usbdevhandle_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE:
+		mausb_ephandle_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE:
+		mausb_epactivate_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE:
+		mausb_epinactivate_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_RESET:
+		mausb_epreset_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_EP_HANDLE_DELETE:
+		mausb_epdelete_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_MODIFY_EP0:
+		mausb_modifyep0_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS:
+		mausb_setusbdevaddress_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_UPDATE_DEV:
+		mausb_updatedev_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_USB_DEV_RESET:
+		mausb_usbdevreset_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_CANCEL_TRANSFER:
+		mausb_canceltransfer_event_from_user(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_PORT_CHANGED:
+		mausb_port_has_changed_event(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_PING:
+		mausb_send_mgmt_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_MGMT_MSG:
+		mausb_send_mgmt_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_DATA_MSG:
+		mausb_send_data_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG:
+		mausb_receive_data_msg(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_URB_COMPLETE:
+		mausb_complete_urb(event);
+		break;
+	case MAUSB_EVENT_TYPE_SEND_ACK:
+		mausb_send_transfer_ack(dev, event);
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_ITERATOR_RESET:
+		mausb_iterator_reset(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_ITERATOR_SEEK:
+		mausb_iterator_seek(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_DELETE_MA_DEV:
+		mausb_delete_ma_dev(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_USER_FINISHED:
+		mausb_process_user_finished(dev, event);
+		break;
+	case MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES:
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_NONE:
+		mausb_release_event_resources(event);
+		break;
+	case MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT:
+		mausb_complete_timeout_event(dev, event);
+		break;
+	default:
+		break;
+	}
+
+	mausb_notify_completed_user_events(dev->ring_buffer);
+}
+
+static void mausb_hpal_kernel_work(struct work_struct *work)
+{
+	struct mausb_device *dev = container_of(work, struct mausb_device,
+						work);
+	struct mausb_event *event;
+	int status;
+	u16 i;
+	u16 events;
+	u16 completed_events;
+	unsigned long flags;
+	struct mausb_ring_buffer *dev_mausb_ring = dev->ring_buffer;
+
+	spin_lock_irqsave(&dev->num_of_user_events_lock, flags);
+	events = dev->num_of_user_events;
+	completed_events = dev->num_of_completed_events;
+	dev->num_of_user_events = 0;
+	dev->num_of_completed_events = 0;
+	spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags);
+
+	status = mausb_ring_buffer_move_tail(dev_mausb_ring, completed_events);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Dequeue failed, status=%d",
+			status);
+		kref_put(&dev->refcount, mausb_release_ma_dev_async);
+		return;
+	}
+
+	for (i = 0; i < events; ++i) {
+		event = mausb_ring_current_from_user(dev_mausb_ring);
+		mausb_ring_next_from_user(dev_mausb_ring);
+		mausb_process_event(dev, event);
+	}
+}
+
 static void mausb_socket_disconnect_event(struct work_struct *work)
 {
 	struct mausb_device *dev = container_of(work, struct mausb_device,
 						socket_disconnect_work);
 	struct mausb_event event;
+	int status;
 
 	dev_info(mausb_host_dev.this_device, "Disconnect madev_addr=%d",
 		 dev->madev_addr);
@@ -361,6 +562,11 @@ static void mausb_socket_disconnect_event(struct work_struct *work)
 		event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED;
 		event.data.device_id = dev->id;
 
+		status = mausb_enqueue_event_to_user(dev, &event);
+
+		dev_info(mausb_host_dev.this_device, "Network disconnected notification sent status=%d",
+			 status);
+
 		dev_info(mausb_host_dev.this_device, "Releasing MAUSB device ref");
 		kref_put(&dev->refcount, mausb_release_ma_dev_async);
 	}
@@ -426,6 +632,13 @@ static void mausb_delete_madev(struct work_struct *work)
 
 		mausb_insert_event(dev, &mausb_completion);
 
+		status = mausb_enqueue_event_to_user(dev, &event);
+		if (status < 0) {
+			mausb_remove_event(dev, &mausb_completion);
+			dev_err(mausb_host_dev.this_device, "Ring buffer full, enqueue failed");
+			return;
+		}
+
 		dev_dbg(mausb_host_dev.this_device, "Deleting MAUSB device...");
 
 		status = wait_for_completion_interruptible_timeout(&completion,
@@ -449,10 +662,14 @@ static void mausb_delete_madev(struct work_struct *work)
 
 	mausb_clear_hcd_madev(dev->port_number);
 
+	mausb_ring_buffer_cleanup(dev->ring_buffer);
+	mausb_ring_buffer_destroy(dev->ring_buffer);
+
 	mausb_remove_madev_from_list(dev->madev_addr);
 
 	put_net(dev->net_ns);
 
+	kfree(dev->ring_buffer);
 	kfree(dev);
 	mausb_signal_empty_mss();
 
@@ -463,6 +680,7 @@ static void mausb_ping_work(struct work_struct *work)
 {
 	struct mausb_device *dev = container_of(work, struct mausb_device,
 						ping_work);
+	int status = 0;
 
 	if (mausb_start_connection_timer(dev) < 0) {
 		dev_err(mausb_host_dev.this_device, "Session timeout - disconnnecting device madev_addr=%d",
@@ -471,6 +689,12 @@ static void mausb_ping_work(struct work_struct *work)
 		queue_work(dev->workq, &dev->hcd_disconnect_work);
 		return;
 	}
+
+	status = mausb_ping_event_to_user(dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Ring buffer full");
+		return;
+	}
 }
 
 static void mausb_heartbeat_work(struct work_struct *work)
@@ -576,6 +800,7 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
 
 	dev->workq = workq;
 
+	INIT_WORK(&dev->work, mausb_hpal_kernel_work);
 	INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event);
 	INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event);
 	INIT_WORK(&dev->madev_delete_work, mausb_delete_madev);
@@ -601,6 +826,15 @@ mausb_create_madev(struct mausb_device_address dev_addr, u8 madev_address,
 	dev->madev_addr = madev_address;
 	dev->net_ns = get_net(current->nsproxy->net_ns);
 
+	if (!list_empty(&mss.available_ring_buffers)) {
+		dev->ring_buffer = container_of(mss.available_ring_buffers.next,
+						struct mausb_ring_buffer,
+						list_entry);
+		list_del(mss.available_ring_buffers.next);
+	} else {
+		dev_alert(mausb_host_dev.this_device, "Ring buffer for mausb device is not available!");
+	}
+
 	list_add_tail(&dev->list_entry, &mss.madev_list);
 
 	reinit_completion(&mss.empty);
@@ -660,6 +894,14 @@ int mausb_initiate_dev_connection(struct mausb_device_address dev_addr,
 	return 0;
 }
 
+void mausb_on_madev_connected(struct mausb_device *dev)
+{
+	struct mausb_event mausb_event;
+
+	mausb_dev_reset_req_event(&mausb_event);
+	mausb_enqueue_event_to_user(dev, &mausb_event);
+}
+
 int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 				  u16 num_of_completed)
 {
@@ -684,9 +926,29 @@ int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
 	return 0;
 }
 
+int mausb_enqueue_event_to_user(struct mausb_device *dev,
+				struct mausb_event *event)
+{
+	int status;
+
+	event->madev_addr = dev->madev_addr;
+	status = mausb_ring_buffer_put(dev->ring_buffer, event);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Ring buffer operation failed");
+		mausb_cleanup_ring_buffer_event(event);
+		return status;
+	}
+
+	mausb_notify_ring_events(dev->ring_buffer);
+	dev_vdbg(mausb_host_dev.this_device, "User-space notification sent.");
+
+	return 0;
+}
+
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request)
 {
+	int status;
 	struct mausb_event mausb_event;
 
 	mausb_event.type   = MAUSB_EVENT_TYPE_SEND_DATA_MSG;
@@ -729,7 +991,12 @@ int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 		       &request->dev->route, sizeof(request->dev->route));
 	}
 
-	return 0;
+	status = mausb_enqueue_event_to_user(dev, &mausb_event);
+	if (status < 0)
+		dev_err(&request->dev->dev, "Failed to enqueue event to user-space ep_handle=%#x, status=%d",
+			mausb_event.data.ep_handle, status);
+
+	return status;
 }
 
 void mausb_complete_request(struct urb *urb, u32 actual_length, int status)
@@ -842,6 +1109,17 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
 	dev_vdbg(mausb_host_dev.this_device, "urb=%p, ep_handle=%#x, dev_handle=%#x",
 		 urb, ep_ctx->ep_handle, ep_ctx->dev_handle);
 
+	if (!usb_endpoint_xfer_isoc(&urb->ep->desc)) {
+		status = mausb_canceltransfer_event_to_user(ep_ctx->ma_dev,
+							    ep_ctx->dev_handle,
+							    ep_ctx->ep_handle,
+							    (uintptr_t)urb);
+		if (status < 0) {
+			dev_err(mausb_host_dev.this_device, "Failed to enqueue cancel transfer to user");
+			goto complete_urb;
+		}
+	}
+
 	memset(&mausb_event, 0, sizeof(mausb_event));
 
 	mausb_event.type   = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER;
@@ -856,6 +1134,13 @@ static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work)
 						MAUSB_DATA_MSG_DIRECTION_IN :
 						MAUSB_DATA_MSG_DIRECTION_OUT);
 
+	status = mausb_enqueue_event_to_user(ep_ctx->ma_dev, &mausb_event);
+	if (status < 0) {
+		dev_alert(mausb_host_dev.this_device, "Failed to enqueue event to user-space ep_handle=%#x, status=%d",
+			  mausb_event.data.ep_handle, status);
+		goto complete_urb;
+	}
+
 	if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) {
 		dev_alert(mausb_host_dev.this_device, "Failed to insert in tree urb=%p ep_handle=%#x, status=%d",
 			  urb, mausb_event.data.ep_handle, status);
@@ -917,6 +1202,7 @@ void mausb_deinitialize_mss(void)
 
 	wait_for_completion(&mss.empty);
 	dev_dbg(mausb_host_dev.this_device, "Waiting for completion on disconnect_event ended");
+	mausb_stop_ring_events();
 
 	timeout = wait_for_completion_timeout(&mss.client_stopped, timeout);
 	dev_info(mausb_host_dev.this_device, "Remaining time after waiting for stopping client %ld",
@@ -1106,7 +1392,6 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
 int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 {
 	struct mausb_urb_ctx *urb_ctx;
-	int status = 0;
 
 	if (event->status != 0) {
 		dev_err(mausb_host_dev.this_device, "Event %d failed with status %d",
@@ -1116,15 +1401,13 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 	}
 
 	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
-
 	if (!urb_ctx) {
 		/* Transfer will be deleted from dequeue task */
 		dev_warn(mausb_host_dev.this_device, "Urb is already cancelled for event=%d",
 			 event->type);
-		return status;
 	}
 
-	return status;
+	return 0;
 }
 
 int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
@@ -1144,7 +1427,6 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 	}
 
 	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
-
 	if (!urb_ctx)
 		dev_warn(mausb_host_dev.this_device, "Urb is already cancelled");
 
@@ -1283,6 +1565,7 @@ static void mausb_connect_callback(struct mausb_device *dev,
 	if (channel == MAUSB_ISOCH_CHANNEL) {
 		dev->channel_map[MAUSB_INTR_CHANNEL] =
 				dev->channel_map[MAUSB_CTRL_CHANNEL];
+		mausb_on_madev_connected(dev);
 	}
 }
 
@@ -1310,6 +1593,16 @@ static void mausb_handle_receive_event(struct mausb_device *dev,
 	}
 
 	mausb_reset_connection_timer(dev);
+
+	status = mausb_msg_received_event(&event,
+					  (struct ma_usb_hdr_common *)data,
+					  channel);
+	if (status == 0)
+		status = mausb_enqueue_event_to_user(dev, &event);
+
+	if (status < 0)
+		dev_err(mausb_host_dev.this_device, "Failed to enqueue, status=%d",
+			status);
 }
 
 void mausb_ip_callback(void *ctx, enum mausb_channel channel,
@@ -1619,3 +1912,154 @@ u32 mausb_data_iterator_length(struct mausb_data_iter *iterator)
 {
 	return iterator->length;
 }
+
+static int mausb_ring_buffer_get(struct mausb_ring_buffer *ring,
+				 struct mausb_event *event)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 1) {
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	memcpy(event, ring->to_user_buffer + ring->tail, sizeof(*event));
+	dev_vdbg(mausb_host_dev.this_device, "HEAD=%d, TAIL=%d", ring->head,
+		 ring->tail);
+	ring->tail = (ring->tail + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+	dev_vdbg(mausb_host_dev.this_device, "HEAD=%d, TAIL=%d", ring->head,
+		 ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+int mausb_ring_buffer_init(struct mausb_ring_buffer *ring)
+{
+	unsigned int page_order =
+		mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+				     sizeof(struct mausb_event));
+	ring->to_user_buffer =
+		(struct mausb_event *)__get_free_pages(GFP_KERNEL, page_order);
+	if (!ring->to_user_buffer)
+		return -ENOMEM;
+	ring->from_user_buffer = ring->to_user_buffer + MAUSB_RING_BUFFER_SIZE;
+	ring->head = 0;
+	ring->tail = 0;
+	ring->current_from_user = 0;
+	ring->buffer_full = false;
+	spin_lock_init(&ring->lock);
+
+	return 0;
+}
+
+int mausb_ring_buffer_put(struct mausb_ring_buffer *ring,
+			  struct mausb_event *event)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+
+	if (ring->buffer_full) {
+		dev_err(mausb_host_dev.this_device, "Ring buffer is full");
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+
+	if (CIRC_SPACE(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 2) {
+		dev_err(mausb_host_dev.this_device, "Ring buffer capacity exceeded, disconnecting device");
+		ring->buffer_full = true;
+		mausb_disconect_event_unsafe(ring, event->madev_addr);
+		ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	memcpy(ring->to_user_buffer + ring->head, event, sizeof(*event));
+	dev_vdbg(mausb_host_dev.this_device, "HEAD=%d, TAIL=%d",
+		 ring->head, ring->tail);
+	ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1);
+	dev_vdbg(mausb_host_dev.this_device, "HEAD=%d, TAIL=%d",
+		 ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < count) {
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return -ENOSPC;
+	}
+	dev_vdbg(mausb_host_dev.this_device, "old HEAD=%d, TAIL=%d",
+		 ring->head, ring->tail);
+	ring->tail = (ring->tail + (int)count) & (MAUSB_RING_BUFFER_SIZE - 1);
+	dev_vdbg(mausb_host_dev.this_device, "new HEAD=%d, TAIL=%d",
+		 ring->head, ring->tail);
+	spin_unlock_irqrestore(&ring->lock, flags);
+	return 0;
+}
+
+void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring)
+{
+	struct mausb_event event;
+
+	while (mausb_ring_buffer_get(ring, &event) == 0)
+		mausb_cleanup_ring_buffer_event(&event);
+}
+
+void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring)
+{
+	unsigned int page_order =
+		mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+				     sizeof(struct mausb_event));
+	if (ring && ring->to_user_buffer)
+		free_pages((unsigned long)ring->to_user_buffer, page_order);
+}
+
+void mausb_cleanup_ring_buffer_event(struct mausb_event *event)
+{
+	dev_dbg(mausb_host_dev.this_device, "Cleanup ring buffer event=%d",
+		event->type);
+	switch (event->type) {
+	case MAUSB_EVENT_TYPE_SEND_DATA_MSG:
+		mausb_cleanup_send_data_msg_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG:
+		mausb_cleanup_received_data_msg_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER:
+		mausb_cleanup_delete_data_transfer_event(event);
+		break;
+	case MAUSB_EVENT_TYPE_NONE:
+		break;
+	default:
+		dev_warn(mausb_host_dev.this_device, "Unknown event type");
+		break;
+	}
+}
+
+void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring,
+				  uint8_t madev_addr)
+{
+	struct mausb_event disconnect_event;
+	struct mausb_device *dev = mausb_get_dev_from_addr_unsafe(madev_addr);
+
+	if (!dev) {
+		dev_err(mausb_host_dev.this_device, "Device not found, madev_addr=%#x",
+			madev_addr);
+		return;
+	}
+
+	disconnect_event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
+	disconnect_event.status = -EINVAL;
+	disconnect_event.madev_addr = madev_addr;
+
+	memcpy(ring->to_user_buffer + ring->head, &disconnect_event,
+	       sizeof(disconnect_event));
+
+	dev_dbg(mausb_host_dev.this_device, "Adding hcd_disconnect_work to dev workq, madev_addr=%#x",
+		madev_addr);
+	queue_work(dev->workq, &dev->hcd_disconnect_work);
+}
diff --git a/drivers/usb/host/mausb/hpal.h b/drivers/usb/host/mausb/hpal.h
index a04ed120ba5e..ac684f9b3612 100644
--- a/drivers/usb/host/mausb/hpal.h
+++ b/drivers/usb/host/mausb/hpal.h
@@ -5,7 +5,7 @@
 #ifndef __MAUSB_HPAL_H__
 #define __MAUSB_HPAL_H__
 
-#include <linux/kref.h>
+#include <linux/miscdevice.h>
 #include <linux/suspend.h>
 #include <linux/usb.h>
 
@@ -26,6 +26,7 @@
 
 extern struct mss mss;
 extern struct mausb_hcd *mhcd;
+extern struct miscdevice mausb_host_dev;
 
 enum mausb_isoch_header_format_size {
 	MAUSB_ISOCH_SHORT_FORMAT_SIZE	 = 4,
@@ -67,6 +68,7 @@ struct mss {
 struct mausb_device {
 	struct mausb_device_address dev_addr;
 	struct net		    *net_ns;
+	struct mausb_ring_buffer    *ring_buffer;
 	struct list_head	    list_entry;
 
 	struct mausb_ip_ctx *mgmt_channel;
@@ -133,6 +135,10 @@ static inline u64 mausb_event_id(struct mausb_device *dev)
 
 int mausb_initiate_dev_connection(struct mausb_device_address device_address,
 				  u8 madev_address);
+int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events,
+				  u16 num_of_completed);
+int mausb_enqueue_event_to_user(struct mausb_device *dev,
+				struct mausb_event *event);
 int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle,
 				 struct urb *request);
 int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event,
@@ -282,6 +288,33 @@ void mausb_reset_data_iterator(struct mausb_data_iter *iterator);
 void mausb_uninit_data_iterator(struct mausb_data_iter *iterator);
 void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta);
 
+struct mausb_ring_buffer {
+	atomic_t mausb_ring_events;
+	atomic_t mausb_completed_user_events;
+
+	struct mausb_event *to_user_buffer;
+	int		head;
+	int		tail;
+	spinlock_t	lock; /* Protect ring buffer */
+	u64		id;
+
+	struct mausb_event *from_user_buffer;
+	int current_from_user;
+
+	struct list_head list_entry;
+	bool buffer_full;
+};
+
+int mausb_ring_buffer_init(struct mausb_ring_buffer *ring);
+int mausb_ring_buffer_put(struct mausb_ring_buffer *ring,
+			  struct mausb_event *event);
+int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count);
+void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring);
+void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring);
+void mausb_cleanup_ring_buffer_event(struct mausb_event *event);
+void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring,
+				  uint8_t madev_addr);
+
 static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
 						unsigned int elem_size)
 {
@@ -292,4 +325,16 @@ static inline unsigned int mausb_get_page_order(unsigned int num_of_elems,
 	return order;
 }
 
+static inline
+struct mausb_event *mausb_ring_current_from_user(struct mausb_ring_buffer *ring)
+{
+	return ring->from_user_buffer + ring->current_from_user;
+}
+
+static inline void mausb_ring_next_from_user(struct mausb_ring_buffer *ring)
+{
+	ring->current_from_user = (ring->current_from_user + 1) &
+				  (MAUSB_RING_BUFFER_SIZE - 1);
+}
+
 #endif /* __MAUSB_HPAL_H__ */
diff --git a/drivers/usb/host/mausb/hpal_events.c b/drivers/usb/host/mausb/hpal_events.c
new file mode 100644
index 000000000000..92ab918bb339
--- /dev/null
+++ b/drivers/usb/host/mausb/hpal_events.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_events.h"
+
+#include "hcd.h"
+#include "utils.h"
+
+void mausb_dev_reset_req_event(struct mausb_event *event)
+{
+	event->type = MAUSB_EVENT_TYPE_DEV_RESET;
+}
+
+static int mausb_mgmt_msg_received_event(struct mausb_event *event,
+					 struct ma_usb_hdr_common *hdr,
+					 enum mausb_channel channel)
+{
+	int status = 0;
+
+	dev_info(mausb_host_dev.this_device, "channel=%d, type=%d", channel,
+		 hdr->type);
+	if (hdr->length <= MAUSB_MAX_MGMT_SIZE) {
+		event->type = MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG;
+		memcpy(event->mgmt.mgmt_hdr.hdr, hdr, hdr->length);
+	} else {
+		dev_err(mausb_host_dev.this_device, "MGMT message to long, failed to copy");
+		status = -EINVAL;
+	}
+
+	kfree(hdr);
+	return status;
+}
+
+static int mausb_data_msg_received_event(struct mausb_event *event,
+					 struct ma_usb_hdr_common *hdr,
+					 enum mausb_channel channel)
+{
+	event->type		  = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
+	event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
+	event->data.device_id	  = (u16)((hdr->ssid << 8) | hdr->dev_addr);
+	event->data.ep_handle	  = hdr->handle.epv;
+	event->data.recv_buf	  = (uintptr_t)hdr;
+
+	memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
+
+	if (mausb_ctrl_transfer(hdr) &&
+	    hdr->length <= 2 * MAUSB_TRANSFER_HDR_SIZE) {
+		memcpy(event->data.hdr_ack,
+		       shift_ptr(hdr, MAUSB_TRANSFER_HDR_SIZE),
+		       (size_t)(hdr->length - MAUSB_TRANSFER_HDR_SIZE));
+	}
+
+	return 0;
+}
+
+static int mausb_isoch_msg_received_event(struct mausb_event *event,
+					  struct ma_usb_hdr_common *hdr,
+					  enum mausb_channel channel)
+{
+	event->type		  = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
+	event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
+	event->data.device_id	  = (u16)((hdr->ssid << 8) | hdr->dev_addr);
+	event->data.ep_handle	  = hdr->handle.epv;
+	event->data.recv_buf	  = (uintptr_t)hdr;
+
+	memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
+
+	return 0;
+}
+
+int mausb_msg_received_event(struct mausb_event *event,
+			     struct ma_usb_hdr_common *hdr,
+			     enum mausb_channel channel)
+{
+	dev_vdbg(mausb_host_dev.this_device, "channel=%d, type=%d", channel,
+		 hdr->type);
+	if (mausb_is_management_hdr_type(hdr->type))
+		return mausb_mgmt_msg_received_event(event, hdr, channel);
+	else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(TRANSFER))
+		return mausb_data_msg_received_event(event, hdr, channel);
+	else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(ISOCHTRANSFER))
+		return mausb_isoch_msg_received_event(event, hdr, channel);
+
+	dev_warn(mausb_host_dev.this_device, "Unknown event type event=%d",
+		 hdr->type);
+	kfree(hdr);
+	return -EBADR;
+}
+
+static void mausb_prepare_completion(struct mausb_completion *mausb_completion,
+				     struct completion *completion,
+				     struct mausb_event *event, u64 event_id)
+{
+	init_completion(completion);
+
+	mausb_completion->completion_event = completion;
+	mausb_completion->event_id	   = event_id;
+	mausb_completion->mausb_event	   = event;
+}
+
+static int mausb_wait_for_completion(struct mausb_event *event, u64 event_id,
+				     struct mausb_device *dev)
+{
+	struct completion	completion;
+	struct mausb_completion mausb_completion;
+	long status;
+	unsigned long timeout;
+
+	mausb_prepare_completion(&mausb_completion, &completion, event,
+				 event_id);
+	mausb_insert_event(dev, &mausb_completion);
+
+	status = mausb_enqueue_event_to_user(dev, event);
+	if (status < 0) {
+		mausb_remove_event(dev, &mausb_completion);
+		dev_err(mausb_host_dev.this_device, "Ring buffer full, event_id=%lld",
+			event_id);
+		return (int)status;
+	}
+
+	timeout = msecs_to_jiffies(MANAGEMENT_EVENT_TIMEOUT);
+	status = wait_for_completion_interruptible_timeout(&completion,
+							   timeout);
+
+	mausb_remove_event(dev, &mausb_completion);
+
+	if (status == 0) {
+		queue_work(dev->workq, &dev->socket_disconnect_work);
+		queue_work(dev->workq, &dev->hcd_disconnect_work);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
+				     u8 device_speed,
+				     u32 route_string,
+				     u16 hub_dev_handle,
+				     u16 parent_hs_hub_dev_handle,
+				     u16 parent_hs_hub_port, u16 mtt,
+				     u8 lse, s32 *usb_dev_handle)
+{
+	struct mausb_event event;
+	int status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_USB_DEV_HANDLE;
+	event.mgmt.dev_handle.device_speed	 = device_speed;
+	event.mgmt.dev_handle.route_string	 = route_string;
+	event.mgmt.dev_handle.hub_dev_handle	 = hub_dev_handle;
+	event.mgmt.dev_handle.parent_hs_hub_port = parent_hs_hub_port;
+	event.mgmt.dev_handle.mtt		 = mtt;
+	event.mgmt.dev_handle.lse		 = lse;
+	event.mgmt.dev_handle.event_id		 = event_id;
+	event.madev_addr			 = dev->madev_addr;
+	event.mgmt.dev_handle.parent_hs_hub_dev_handle =
+						   parent_hs_hub_dev_handle;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Usbdevhandle failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*usb_dev_handle = event.mgmt.dev_handle.dev_handle;
+
+	return 0;
+}
+
+int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.dev_handle.event_id);
+}
+
+int mausb_ephandle_event_to_user(struct mausb_device *dev,
+				 u16 device_handle,
+				 u16 descriptor_size, void *descriptor,
+				 u16 *ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			     = MAUSB_EVENT_TYPE_EP_HANDLE;
+	event.mgmt.ep_handle.device_handle   = device_handle;
+	event.mgmt.ep_handle.descriptor_size = descriptor_size;
+	event.mgmt.ep_handle.event_id	     = event_id;
+	event.madev_addr		     = dev->madev_addr;
+
+	memcpy(event.mgmt.ep_handle.descriptor, descriptor, descriptor_size);
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Ephandle failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*ep_handle = event.mgmt.ep_handle.ep_handle;
+
+	return 0;
+}
+
+int mausb_ephandle_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_handle.event_id);
+}
+
+int mausb_epactivate_event_to_user(struct mausb_device *dev,
+				   u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE;
+	event.mgmt.ep_activate.device_handle = device_handle;
+	event.mgmt.ep_activate.ep_handle     = ep_handle;
+	event.mgmt.ep_activate.event_id	     = event_id;
+	event.madev_addr		     = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Epactivate failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epactivate_event_from_user(struct mausb_device *dev,
+				     struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+		event->mgmt.ep_activate.event_id);
+}
+
+int mausb_epinactivate_event_to_user(struct mausb_device *dev,
+				     u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE;
+	event.mgmt.ep_inactivate.device_handle	= device_handle;
+	event.mgmt.ep_inactivate.ep_handle	= ep_handle;
+	event.mgmt.ep_inactivate.event_id	= event_id;
+	event.madev_addr			= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Epinactivate failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epinactivate_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+				  event->mgmt.ep_inactivate.event_id);
+}
+
+int mausb_epreset_event_to_user(struct mausb_device *dev,
+				u16 device_handle, u16 ep_handle,
+				u8 tsp_flag)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			  = MAUSB_EVENT_TYPE_EP_HANDLE_RESET;
+	event.mgmt.ep_reset.device_handle = device_handle;
+	event.mgmt.ep_reset.ep_handle	  = ep_handle;
+	event.mgmt.ep_reset.tsp		  = tsp_flag;
+	event.mgmt.ep_reset.event_id	  = event_id;
+	event.madev_addr		  = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Epreset failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epreset_event_from_user(struct mausb_device *dev,
+				  struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_reset.event_id);
+}
+
+int mausb_epdelete_event_to_user(struct mausb_device *dev,
+				 u16 device_handle, u16 ep_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			   = MAUSB_EVENT_TYPE_EP_HANDLE_DELETE;
+	event.mgmt.ep_delete.device_handle = device_handle;
+	event.mgmt.ep_delete.ep_handle	   = ep_handle;
+	event.mgmt.ep_delete.event_id	   = event_id;
+	event.madev_addr		   = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Epdelete failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_epdelete_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.ep_delete.event_id);
+}
+
+int mausb_modifyep0_event_to_user(struct mausb_device *dev,
+				  u16 device_handle, u16 *ep_handle,
+				  __le16 max_packet_size)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type				= MAUSB_EVENT_TYPE_MODIFY_EP0;
+	event.mgmt.modify_ep0.device_handle	= device_handle;
+	event.mgmt.modify_ep0.ep_handle		= *ep_handle;
+	event.mgmt.modify_ep0.max_packet_size	= max_packet_size;
+	event.mgmt.modify_ep0.event_id		= event_id;
+	event.madev_addr			= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "ModifyEP0 failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	if (event.status < 0)
+		return event.status;
+
+	*ep_handle = event.mgmt.modify_ep0.ep_handle;
+
+	return 0;
+}
+
+int mausb_modifyep0_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.modify_ep0.event_id);
+}
+
+int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
+					 u16 device_handle,
+					 u16 response_timeout)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS;
+	event.mgmt.set_usb_dev_address.device_handle	= device_handle;
+	event.mgmt.set_usb_dev_address.response_timeout	= response_timeout;
+	event.mgmt.set_usb_dev_address.event_id		= event_id;
+	event.madev_addr				= dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "SetUSBDevAddress failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
+					   struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+		event->mgmt.set_usb_dev_address.event_id);
+}
+
+static void
+mausb_init_device_descriptor(struct ma_usb_updatedevreq_desc *update_descriptor,
+			     struct usb_device_descriptor *device_descriptor)
+{
+	update_descriptor->usb20.bLength = device_descriptor->bLength;
+	update_descriptor->usb20.bDescriptorType =
+					device_descriptor->bDescriptorType;
+	update_descriptor->usb20.bcdUSB = device_descriptor->bcdUSB;
+	update_descriptor->usb20.bDeviceClass =
+					device_descriptor->bDeviceClass;
+	update_descriptor->usb20.bDeviceSubClass =
+					device_descriptor->bDeviceSubClass;
+	update_descriptor->usb20.bDeviceProtocol =
+					device_descriptor->bDeviceProtocol;
+	update_descriptor->usb20.bMaxPacketSize0 =
+					device_descriptor->bMaxPacketSize0;
+	update_descriptor->usb20.idVendor  = device_descriptor->idVendor;
+	update_descriptor->usb20.idProduct = device_descriptor->idProduct;
+	update_descriptor->usb20.bcdDevice = device_descriptor->bcdDevice;
+	update_descriptor->usb20.iManufacturer =
+					device_descriptor->iManufacturer;
+	update_descriptor->usb20.iProduct  = device_descriptor->iProduct;
+	update_descriptor->usb20.iSerialNumber =
+					device_descriptor->iSerialNumber;
+	update_descriptor->usb20.bNumConfigurations =
+					device_descriptor->bNumConfigurations;
+}
+
+int mausb_updatedev_event_to_user(struct mausb_device *dev,
+				  u16 device_handle,
+				  u16 max_exit_latency, u8 hub,
+				  u8 number_of_ports, u8 mtt,
+				  u8 ttt, u8 integrated_hub_latency,
+				  struct usb_device_descriptor *dev_descriptor)
+{
+	struct mausb_event event;
+	int status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type = MAUSB_EVENT_TYPE_UPDATE_DEV;
+	event.mgmt.update_dev.device_handle	     = device_handle;
+	event.mgmt.update_dev.max_exit_latency	     = max_exit_latency;
+	event.mgmt.update_dev.hub		     = hub;
+	event.mgmt.update_dev.number_of_ports	     = number_of_ports;
+	event.mgmt.update_dev.mtt		     = mtt;
+	event.mgmt.update_dev.ttt		     = ttt;
+	event.mgmt.update_dev.integrated_hub_latency = integrated_hub_latency;
+	event.mgmt.update_dev.event_id		     = event_id;
+	event.madev_addr			     = dev->madev_addr;
+
+	mausb_init_device_descriptor(&event.mgmt.update_dev.update_descriptor,
+				     dev_descriptor);
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "UpdateDev failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_updatedev_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event, event->mgmt.update_dev.event_id);
+}
+
+int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
+					 u16 dev_handle)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type = MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT;
+	event.mgmt.usb_dev_disconnect.device_handle = dev_handle;
+	event.madev_addr			    = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		dev_err(mausb_host_dev.this_device, "Ring buffer full, usbdevdisconnect failed");
+
+	return status;
+}
+
+int mausb_ping_event_to_user(struct mausb_device *dev)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type	 = MAUSB_EVENT_TYPE_PING;
+	event.madev_addr = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		dev_err(mausb_host_dev.this_device, "Ring buffer full, devdisconnect failed");
+
+	return status;
+}
+
+__attribute__((unused))
+static int mausb_devdisconnect_event_to_user(struct mausb_device *dev)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type	 = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
+	event.madev_addr = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0)
+		dev_err(mausb_host_dev.this_device, "Ring buffer full, devdisconnect failed");
+
+	return status;
+}
+
+int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
+				    u16 device_handle)
+{
+	struct mausb_event event;
+	int  status;
+	u64 event_id = mausb_event_id(dev);
+
+	event.type			       = MAUSB_EVENT_TYPE_USB_DEV_RESET;
+	event.mgmt.usb_dev_reset.device_handle = device_handle;
+	event.mgmt.usb_dev_reset.event_id      = event_id;
+	event.madev_addr		       = dev->madev_addr;
+
+	status = mausb_wait_for_completion(&event, event_id, dev);
+
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "UsbDevReset failed, event_id=%lld",
+			event_id);
+		return status;
+	}
+
+	return event.status;
+}
+
+int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
+				      struct mausb_event *event)
+{
+	return mausb_signal_event(dev, event,
+				  event->mgmt.usb_dev_reset.event_id);
+}
+
+int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
+				       u16 device_handle, u16 ep_handle,
+				       uintptr_t urb)
+{
+	struct mausb_event event;
+	int status;
+
+	event.type = MAUSB_EVENT_TYPE_CANCEL_TRANSFER;
+	event.mgmt.cancel_transfer.device_handle = device_handle;
+	event.mgmt.cancel_transfer.ep_handle	 = ep_handle;
+	event.mgmt.cancel_transfer.urb		 = urb;
+	event.madev_addr			 = dev->madev_addr;
+
+	status = mausb_enqueue_event_to_user(dev, &event);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Ring buffer full, canceltransfer failed");
+		return status;
+	}
+
+	return status;
+}
+
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event)
+{
+	return 0;
+}
+
+void mausb_cleanup_send_data_msg_event(struct mausb_event *event)
+{
+	mausb_complete_urb(event);
+}
+
+void mausb_cleanup_received_data_msg_event(struct mausb_event *event)
+{
+	mausb_release_event_resources(event);
+}
+
+void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
+
+	urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
+	if (!urb_ctx) {
+		dev_warn(mausb_host_dev.this_device, "Urb=%p is not in tree",
+			 urb);
+		return;
+	}
+
+	/* Deallocate urb_ctx */
+	mausb_uninit_data_iterator(&urb_ctx->iterator);
+	kfree(urb_ctx);
+
+	urb->status = -EPROTO;
+	urb->actual_length = 0;
+	atomic_dec(&urb->use_count);
+	usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
+}
diff --git a/drivers/usb/host/mausb/hpal_events.h b/drivers/usb/host/mausb/hpal_events.h
new file mode 100644
index 000000000000..1535a814c2a7
--- /dev/null
+++ b/drivers/usb/host/mausb/hpal_events.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_EVENTS_H__
+#define __MAUSB_HPAL_EVENTS_H__
+
+#include "hpal.h"
+
+#define MANAGEMENT_EVENT_TIMEOUT 3000
+
+int mausb_msg_received_event(struct mausb_event *event,
+			     struct ma_usb_hdr_common *hdr,
+			     enum mausb_channel channel);
+int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
+				     u8 device_speed,
+				     u32 route_string,
+				     u16 hub_dev_handle,
+				     u16 parent_hs_hub_dev_handle,
+				     u16 parent_hs_hub_port, u16 mtt,
+				     u8 lse, s32 *usb_dev_handle);
+int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event);
+int mausb_ephandle_event_to_user(struct mausb_device *dev, u16 device_handle,
+				 u16 descriptor_size, void *descriptor,
+				 u16 *ep_handle);
+int mausb_ephandle_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event);
+int mausb_epactivate_event_to_user(struct mausb_device *dev,
+				   u16 device_handle, u16 ep_handle);
+int mausb_epactivate_event_from_user(struct mausb_device *dev,
+				     struct mausb_event *event);
+int mausb_epinactivate_event_to_user(struct mausb_device *dev,
+				     u16 device_handle,
+				     u16 ep_handle);
+int mausb_epinactivate_event_from_user(struct mausb_device *dev,
+				       struct mausb_event *event);
+int mausb_epreset_event_to_user(struct mausb_device *dev,
+				u16 device_handle, u16 ep_handle,
+				u8 tsp_flag);
+int mausb_epreset_event_from_user(struct mausb_device *dev,
+				  struct mausb_event *event);
+int mausb_epdelete_event_to_user(struct mausb_device *dev,
+				 u16 device_handle, u16 ep_handle);
+int mausb_epdelete_event_from_user(struct mausb_device *dev,
+				   struct mausb_event *event);
+int mausb_modifyep0_event_to_user(struct mausb_device *dev,
+				  u16 device_handle, u16 *ep_handle,
+				  __le16 max_packet_size);
+int mausb_modifyep0_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event);
+int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
+					 u16 device_handle,
+					 u16 response_timeout);
+int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
+					   struct mausb_event *event);
+int mausb_updatedev_event_to_user(struct mausb_device *dev,
+				  u16 device_handle,
+				  u16 max_exit_latency, u8 hub,
+				  u8 number_of_ports, u8 mtt,
+				  u8 ttt, u8 integrated_hub_latency,
+				  struct usb_device_descriptor *dev_descriptor);
+int mausb_updatedev_event_from_user(struct mausb_device *dev,
+				    struct mausb_event *event);
+int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
+					 u16 dev_handle);
+int mausb_ping_event_to_user(struct mausb_device *dev);
+int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
+				    u16 device_handle);
+int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
+				      struct mausb_event *event);
+int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
+				       u16 device_handle, u16 ep_handle,
+				       uintptr_t urb);
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event);
+
+void mausb_dev_reset_req_event(struct mausb_event *event);
+int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
+					 struct mausb_event *event);
+void mausb_cleanup_send_data_msg_event(struct mausb_event *event);
+void mausb_cleanup_received_data_msg_event(struct mausb_event *event);
+void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_EVENTS_H__ */
diff --git a/drivers/usb/host/mausb/mausb_driver_status.h b/drivers/usb/host/mausb/mausb_driver_status.h
new file mode 100644
index 000000000000..9b55befe9cae
--- /dev/null
+++ b/drivers/usb/host/mausb/mausb_driver_status.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_MAUSB_DRIVER_STATUS_H__
+#define __MAUSB_MAUSB_DRIVER_STATUS_H__
+
+#define MAUSB_DRIVER_BAD_READ_BUFFER_SIZE  -1
+#define MAUSB_DRIVER_RING_EVENTS_STOPPED   -2
+#define MAUSB_DRIVER_SYSTEM_SUSPENDED      -3
+#define MAUSB_DRIVER_COPY_TO_BUFFER_FAILED -4
+
+#define MAUSB_DRIVER_READ_TIMEOUT 0
+#define MAUSB_DRIVER_READ_ERROR  -1
+#define MAUSB_DRIVER_WRITE_ERROR  -2
+
+#endif /* __MAUSB_MAUSB_DRIVER_STATUS_H__ */
diff --git a/drivers/usb/host/mausb/utils.c b/drivers/usb/host/mausb/utils.c
index 1cfa2140311e..05ecf03ebdca 100644
--- a/drivers/usb/host/mausb/utils.c
+++ b/drivers/usb/host/mausb/utils.c
@@ -5,13 +5,51 @@
 #include "utils.h"
 
 #include <linux/fs.h>
+#include <linux/miscdevice.h>
 #include <linux/slab.h>
 
+#include "mausb_driver_status.h"
+
 #define MAUSB_KERNEL_DEV_NAME "mausb_host"
 #define MAUSB_READ_DEVICE_TIMEOUT_MS 500
 
 struct miscdevice mausb_host_dev;
 
+static void mausb_vm_close(struct vm_area_struct *vma)
+{
+	struct mausb_ring_buffer *buffer = NULL, *next = NULL;
+	unsigned long flags = 0;
+	u64 ring_buffer_id = *(u64 *)(vma->vm_private_data);
+
+	dev_info(mausb_host_dev.this_device, "Releasing ring buffer with id: %llu",
+		 ring_buffer_id);
+	spin_lock_irqsave(&mss.lock, flags);
+	list_for_each_entry_safe(buffer, next, &mss.available_ring_buffers,
+				 list_entry) {
+		if (buffer->id == ring_buffer_id) {
+			list_del(&buffer->list_entry);
+			mausb_ring_buffer_destroy(buffer);
+			kfree(buffer);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&mss.lock, flags);
+}
+
+/*
+ * mausb_vm_fault is called the first time a memory area is accessed which is
+ * not in memory
+ */
+static vm_fault_t mausb_vm_fault(struct vm_fault *vmf)
+{
+	return 0;
+}
+
+static const struct vm_operations_struct mausb_vm_ops = {
+	.close = mausb_vm_close,
+	.fault = mausb_vm_fault,
+};
+
 static int mausb_host_dev_open(struct inode *inode, struct file *filp)
 {
 	filp->private_data = NULL;
@@ -27,9 +65,214 @@ static int mausb_host_dev_release(struct inode *inode, struct file *filp)
 	return 0;
 }
 
+static ssize_t mausb_host_dev_read(struct file *filp, char __user *user_buffer,
+				   size_t size, loff_t *offset)
+{
+	ssize_t num_of_bytes_to_read = MAUSB_MAX_NUM_OF_MA_DEVS *
+				       sizeof(struct mausb_events_notification);
+	unsigned long num_of_bytes_not_copied;
+	int completed_events;
+	int ring_events;
+	struct mausb_ring_buffer *ring_buffer;
+	struct mausb_device	 *dev;
+	struct completion	 *ring_has_events;
+	u8 current_device = 0;
+	s8 fail_ret_val;
+	unsigned long flags;
+	unsigned long timeout;
+	long status;
+
+	/* Reset heartbeat timer events */
+	mausb_reset_heartbeat_cnt();
+
+	if ((ssize_t)size != num_of_bytes_to_read) {
+		dev_alert(mausb_host_dev.this_device, "The actual size differs from the expected number of bytes");
+		fail_ret_val = MAUSB_DRIVER_BAD_READ_BUFFER_SIZE;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			dev_warn(mausb_host_dev.this_device, "Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	/* If suspend/hibernate happened delete all devices */
+	if (atomic_xchg(&mss.num_of_transitions_to_sleep, 0)) {
+		dev_alert(mausb_host_dev.this_device, "Suspend system event detected");
+		fail_ret_val = MAUSB_DRIVER_SYSTEM_SUSPENDED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			dev_warn(mausb_host_dev.this_device, "Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	ring_has_events = &mss.rings_events.mausb_ring_has_events;
+	timeout = msecs_to_jiffies(MAUSB_READ_DEVICE_TIMEOUT_MS);
+	status = wait_for_completion_interruptible_timeout(ring_has_events,
+							   timeout);
+	reinit_completion(ring_has_events);
+
+	if (atomic_read(&mss.rings_events.mausb_stop_reading_ring_events)) {
+		dev_alert(mausb_host_dev.this_device, "Ring events stopped");
+		fail_ret_val = MAUSB_DRIVER_RING_EVENTS_STOPPED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			dev_warn(mausb_host_dev.this_device, "Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	/* There are no new events - waiting for events hit timeout */
+	if (status == 0)
+		return MAUSB_DRIVER_READ_TIMEOUT;
+
+	spin_lock_irqsave(&mss.lock, flags);
+
+	list_for_each_entry(dev, &mss.madev_list, list_entry) {
+		mss.events[current_device].madev_addr = dev->madev_addr;
+		ring_buffer = dev->ring_buffer;
+		ring_events = atomic_xchg(&ring_buffer->mausb_ring_events, 0);
+		completed_events =
+			atomic_xchg(&ring_buffer->mausb_completed_user_events,
+				    0);
+		mss.events[current_device].num_of_events = (u16)ring_events;
+		mss.events[current_device].num_of_completed_events =
+				(u16)completed_events;
+		if (++current_device == MAUSB_MAX_NUM_OF_MA_DEVS)
+			break;
+	}
+
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	num_of_bytes_to_read =
+		(ssize_t)(current_device *
+			  sizeof(struct mausb_events_notification));
+	num_of_bytes_not_copied =
+		copy_to_user(user_buffer, &mss.events,
+			     (unsigned long)num_of_bytes_to_read);
+
+	dev_vdbg(mausb_host_dev.this_device, "num_of_bytes_not_copied %lu, num_of_bytes_to_read %zd",
+		 num_of_bytes_not_copied, num_of_bytes_to_read);
+
+	if (num_of_bytes_not_copied) {
+		fail_ret_val = MAUSB_DRIVER_COPY_TO_BUFFER_FAILED;
+		if (copy_to_user(user_buffer, &fail_ret_val,
+				 sizeof(fail_ret_val)) != 0) {
+			dev_warn(mausb_host_dev.this_device, "Failed to set error code.");
+		}
+		return MAUSB_DRIVER_READ_ERROR;
+	}
+
+	return num_of_bytes_to_read;
+}
+
+static ssize_t mausb_host_dev_write(struct file *filp,
+				    const char __user *buffer, size_t size,
+				    loff_t *offset)
+{
+	ssize_t num_of_bytes_to_write =
+				sizeof(struct mausb_events_notification);
+	struct mausb_events_notification notification;
+	unsigned long flags;
+	struct mausb_device *dev;
+
+	if (size != (size_t)num_of_bytes_to_write) {
+		dev_alert(mausb_host_dev.this_device, "The actual size differs from the expected number of bytes");
+		return MAUSB_DRIVER_WRITE_ERROR;
+	}
+
+	if (copy_from_user(&notification, buffer, size))
+		return MAUSB_DRIVER_WRITE_ERROR;
+
+	spin_lock_irqsave(&mss.lock, flags);
+	dev = mausb_get_dev_from_addr_unsafe(notification.madev_addr);
+
+	if (!dev) {
+		spin_unlock_irqrestore(&mss.lock, flags);
+		return 0;
+	}
+
+	spin_lock(&dev->num_of_user_events_lock);
+	dev->num_of_user_events += notification.num_of_events;
+	dev->num_of_completed_events += notification.num_of_completed_events;
+	spin_unlock(&dev->num_of_user_events_lock);
+
+	queue_work(dev->workq, &dev->work);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return num_of_bytes_to_write;
+}
+
+static inline unsigned long mausb_ring_buffer_length(void)
+{
+	int page_order = mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE,
+					      sizeof(struct mausb_event));
+	return PAGE_SIZE << page_order;
+}
+
+static int mausb_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	unsigned long size = vma->vm_end - vma->vm_start;
+	int ret;
+	struct page *page = NULL;
+	unsigned long flags = 0;
+	struct mausb_ring_buffer *ring_buffer = kzalloc(sizeof(*ring_buffer),
+							GFP_KERNEL);
+	if (!ring_buffer)
+		return -ENOMEM;
+
+	ret = mausb_ring_buffer_init(ring_buffer);
+	if (ret < 0) {
+		dev_err(mausb_host_dev.this_device, "Ring buffer init failed");
+		goto release_ring_buffer;
+	}
+
+	vma->vm_private_data = kzalloc(sizeof(ring_buffer->id), GFP_KERNEL);
+	if (!vma->vm_private_data) {
+		ret = -ENOMEM;
+		goto release_ring_buffer;
+	}
+
+	filp->private_data = vma->vm_private_data;
+
+	if (size > mausb_ring_buffer_length()) {
+		dev_err(mausb_host_dev.this_device, "Invalid memory size to map");
+		ret = -EINVAL;
+		goto release_ring_buffer;
+	}
+
+	vma->vm_ops = &mausb_vm_ops;
+
+	page = virt_to_page(ring_buffer->to_user_buffer);
+	ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size,
+			      vma->vm_page_prot);
+	if (ret < 0) {
+		dev_err(mausb_host_dev.this_device, "Could not map the address area");
+		goto release_ring_buffer;
+	}
+
+	spin_lock_irqsave(&mss.lock, flags);
+	ring_buffer->id = mss.ring_buffer_id++;
+	*(u64 *)(vma->vm_private_data) = ring_buffer->id;
+	list_add_tail(&ring_buffer->list_entry, &mss.available_ring_buffers);
+	dev_info(mausb_host_dev.this_device, "Allocated ring buffer with id: %llu",
+		 ring_buffer->id);
+	spin_unlock_irqrestore(&mss.lock, flags);
+
+	return 0;
+
+release_ring_buffer:
+	mausb_ring_buffer_destroy(ring_buffer);
+	kfree(ring_buffer);
+	return ret;
+}
+
 static const struct file_operations mausb_host_dev_fops = {
 	.open	 = mausb_host_dev_open,
 	.release = mausb_host_dev_release,
+	.read	 = mausb_host_dev_read,
+	.write   = mausb_host_dev_write,
+	.mmap	 = mausb_mmap,
 };
 
 int mausb_host_dev_register(void)
@@ -45,3 +288,30 @@ void mausb_host_dev_deregister(void)
 {
 	misc_deregister(&mausb_host_dev);
 }
+
+void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer)
+{
+	int completed;
+
+	completed =
+		atomic_inc_return(&ring_buffer->mausb_completed_user_events);
+	dev_vdbg(mausb_host_dev.this_device, "mausb_completed_user_events INCREMENTED %d",
+		 completed);
+	if (completed > MAUSB_RING_BUFFER_SIZE / 16)
+		complete(&mss.rings_events.mausb_ring_has_events);
+}
+
+void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer)
+{
+	int events;
+
+	events = atomic_inc_return(&ring_buffer->mausb_ring_events);
+	if (events == 1)
+		complete(&mss.rings_events.mausb_ring_has_events);
+}
+
+void mausb_stop_ring_events(void)
+{
+	atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 1);
+	complete(&mss.rings_events.mausb_ring_has_events);
+}
diff --git a/drivers/usb/host/mausb/utils.h b/drivers/usb/host/mausb/utils.h
index e810e691c39d..37bcf73bc590 100644
--- a/drivers/usb/host/mausb/utils.h
+++ b/drivers/usb/host/mausb/utils.h
@@ -5,11 +5,12 @@
 #ifndef __MAUSB_UTILS_H__
 #define __MAUSB_UTILS_H__
 
-#include <linux/miscdevice.h>
-
-extern struct miscdevice mausb_host_dev;
+#include "hpal.h"
 
 int mausb_host_dev_register(void);
 void mausb_host_dev_deregister(void);
+void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer);
+void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer);
+void mausb_stop_ring_events(void);
 
 #endif /* __MAUSB_UTILS_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH v6 8/8] usb: mausb_host: Process MA-USB data packets
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
                         ` (6 preceding siblings ...)
  2020-05-15 12:35       ` [PATCH v6 7/8] usb: mausb_host: MA-USB PAL events processing Vladimir Stankovic
@ 2020-05-15 12:35       ` Vladimir Stankovic
  2020-05-15 13:08       ` [PATCH v6 0/8] Add MA USB Host driver Greg KH
  8 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 12:35 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, linux-usb, mausb-host-devel

Added implementation of MA-USB data&isoch packets processing logic,
both for IN and OUT directions.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
---
 drivers/usb/host/mausb/Makefile    |   1 +
 drivers/usb/host/mausb/hpal.c      |  35 +-
 drivers/usb/host/mausb/hpal_data.c | 713 +++++++++++++++++++++++++++++
 drivers/usb/host/mausb/hpal_data.h |  34 ++
 4 files changed, 780 insertions(+), 3 deletions(-)
 create mode 100644 drivers/usb/host/mausb/hpal_data.c
 create mode 100644 drivers/usb/host/mausb/hpal_data.h

diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
index b0423f5d6a14..5e27c46183f2 100644
--- a/drivers/usb/host/mausb/Makefile
+++ b/drivers/usb/host/mausb/Makefile
@@ -12,3 +12,4 @@ mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
 mausb_host-y += hpal_events.o
+mausb_host-y += hpal_data.o
diff --git a/drivers/usb/host/mausb/hpal.c b/drivers/usb/host/mausb/hpal.c
index 19d74ffb1610..409b1252c58c 100644
--- a/drivers/usb/host/mausb/hpal.c
+++ b/drivers/usb/host/mausb/hpal.c
@@ -7,7 +7,7 @@
 #include <linux/circ_buf.h>
 
 #include "hcd.h"
-#include "hpal_events.h"
+#include "hpal_data.h"
 #include "utils.h"
 
 #define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000
@@ -1392,6 +1392,7 @@ int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event)
 int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 {
 	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
 
 	if (event->status != 0) {
 		dev_err(mausb_host_dev.this_device, "Event %d failed with status %d",
@@ -1405,9 +1406,22 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 		/* Transfer will be deleted from dequeue task */
 		dev_warn(mausb_host_dev.this_device, "Urb is already cancelled for event=%d",
 			 event->type);
+		return status;
 	}
 
-	return 0;
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_send_isoch_in_msg(dev, event);
+		else
+			status = mausb_send_isoch_out_msg(dev, event, urb_ctx);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_send_in_data_msg(dev, event);
+		else
+			status = mausb_send_out_data_msg(dev, event, urb_ctx);
+	}
+
+	return status;
 }
 
 int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
@@ -1427,8 +1441,23 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 	}
 
 	urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb);
-	if (!urb_ctx)
+	if (!urb_ctx) {
+		/* Transfer will be deleted from dequeue task */
 		dev_warn(mausb_host_dev.this_device, "Urb is already cancelled");
+		goto cleanup;
+	}
+
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			mausb_receive_isoch_in_data(dev, event, urb_ctx);
+		else
+			mausb_receive_isoch_out(event);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			mausb_receive_in_data(event, urb_ctx);
+		else
+			mausb_receive_out_data(event, urb_ctx);
+	}
 
 cleanup:
 	mausb_release_event_resources(event);
diff --git a/drivers/usb/host/mausb/hpal_data.c b/drivers/usb/host/mausb/hpal_data.c
new file mode 100644
index 000000000000..3b5169809b3e
--- /dev/null
+++ b/drivers/usb/host/mausb/hpal_data.c
@@ -0,0 +1,713 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_data.h"
+
+#include <linux/slab.h>
+#include <linux/uio.h>
+
+#include "hcd.h"
+#include "hpal.h"
+#include "hpal_events.h"
+#include "utils.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[2];
+	struct urb *urb   = (struct urb *)(event->data.urb);
+	bool setup_packet = (usb_endpoint_xfer_control(&urb->ep->desc) &&
+			     urb->setup_packet);
+	u32 kvec_num = setup_packet ? 2 : 1;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= kvec_num;
+	data_to_send.length	= MAUSB_TRANSFER_HDR_SIZE +
+			(setup_packet ? MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base = event->data.hdr;
+	kvec[0].iov_len  = MAUSB_TRANSFER_HDR_SIZE;
+
+	/* Prepare setup packet kvec */
+	if (setup_packet) {
+		kvec[1].iov_base = urb->setup_packet;
+		kvec[1].iov_len  = MAUSB_CONTROL_SETUP_SIZE;
+	}
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+	struct mausb_data_iter *iterator     = &urb_ctx->iterator;
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	void *buffer;
+	u32 payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE;
+	u32 data_written = 0;
+
+	buffer = shift_ptr(common_hdr, MAUSB_TRANSFER_HDR_SIZE);
+	data_written = mausb_data_iterator_write(iterator, buffer,
+						 payload_size);
+
+	dev_vdbg(mausb_host_dev.this_device, "data_written=%d, payload_size=%d",
+		 data_written, payload_size);
+	event->data.rem_transfer_size -= data_written;
+
+	if (event->data.transfer_eot) {
+		dev_vdbg(mausb_host_dev.this_device, "transfer_size=%d, rem_transfer_size=%d, status=%d",
+			 event->data.transfer_size,
+			 event->data.rem_transfer_size, event->status);
+		mausb_complete_request(urb, event->data.transfer_size -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static int
+mausb_init_data_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				 struct list_head *chunks_list,
+				 u32 *num_of_data_chunks)
+{
+	int status = mausb_add_data_chunk(common_hdr, MAUSB_TRANSFER_HDR_SIZE,
+					  chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int mausb_init_control_data_chunk(struct mausb_event *event,
+					 struct list_head *chunks_list,
+					 u32 *num_of_data_chunks)
+{
+	int status;
+	void *buffer = ((struct urb *)event->data.urb)->setup_packet;
+
+	if (!event->data.first_control_packet)
+		return 0;
+
+	status = mausb_add_data_chunk(buffer, MAUSB_CONTROL_SETUP_SIZE,
+				      chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int
+mausb_prepare_transfer_packet(struct mausb_kvec_data_wrapper *wrapper,
+			      struct mausb_event *event,
+			      struct mausb_data_iter *iterator)
+{
+	u32 num_of_data_chunks		= 0;
+	u32 num_of_payload_data_chunks	= 0;
+	u32 payload_data_size		= 0;
+	int status = 0;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	struct ma_usb_hdr_common *data_hdr = (struct ma_usb_hdr_common *)
+			event->data.hdr;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_data_out_header_chunk(data_hdr, &chunks_list,
+					     &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/*
+	 * Initialize data chunk for MAUSB control setup packet and
+	 * add it to chunks list
+	 */
+	if (mausb_init_control_data_chunk(event, &chunks_list,
+					  &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size =
+			((struct ma_usb_hdr_common *)event->data.hdr)->length -
+			 MAUSB_TRANSFER_HDR_SIZE -
+			 (event->data.first_control_packet ?
+			  MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	if (mausb_data_iterator_read(iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks: /* Cleanup all allocated data chunks */
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	int status;
+	struct mausb_kvec_data_wrapper data;
+	enum mausb_channel channel;
+
+	status = mausb_prepare_transfer_packet(&data, event,
+					       &urb_ctx->iterator);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "Failed to prepare transfer packet");
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data);
+
+	kfree(data.kvec);
+
+	return status;
+}
+
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	dev_vdbg(mausb_host_dev.this_device, "transfer_size=%d, rem_transfer_size=%d, status=%d",
+		 event->data.transfer_size, event->data.rem_transfer_size,
+		 event->status);
+
+	if (event->data.transfer_eot) {
+		mausb_complete_request(urb, urb->transfer_buffer_length -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static inline u32
+__mausb_isoch_prepare_read_size_block(struct ma_usb_hdr_isochreadsizeblock_std *
+				      isoch_readsize_block, struct urb *urb)
+{
+	u32 i;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+
+	if (number_of_packets == 0)
+		return 0;
+
+	isoch_readsize_block->service_intervals  = number_of_packets;
+	isoch_readsize_block->max_segment_length =
+					(u32)urb->iso_frame_desc[0].length;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+
+	return sizeof(struct ma_usb_hdr_isochreadsizeblock_std);
+}
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	u32 read_size_block_length = 0;
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[MAUSB_ISOCH_IN_KVEC_NUM];
+	struct ma_usb_hdr_isochtransfer_optional opt_isoch_hdr;
+	struct ma_usb_hdr_isochreadsizeblock_std isoch_readsize_block;
+	struct ma_usb_hdr_common *hdr =
+				(struct ma_usb_hdr_common *)event->data.hdr;
+	struct urb *urb = (struct urb *)event->data.urb;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= 0;
+	data_to_send.length	= 0;
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base     = event->data.hdr;
+	kvec[0].iov_len	     = MAUSB_TRANSFER_HDR_SIZE;
+	data_to_send.length += (u32)kvec[0].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare optional header kvec */
+	opt_isoch_hdr.timestamp = MA_USB_TRANSFER_RESERVED;
+	opt_isoch_hdr.mtd	= MA_USB_TRANSFER_RESERVED;
+
+	kvec[1].iov_base     = &opt_isoch_hdr;
+	kvec[1].iov_len	     = sizeof(struct ma_usb_hdr_isochtransfer_optional);
+	data_to_send.length += (u32)kvec[1].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare read size blocks */
+	read_size_block_length =
+		__mausb_isoch_prepare_read_size_block(&isoch_readsize_block,
+						      urb);
+	if (read_size_block_length > 0) {
+		kvec[2].iov_base     = &isoch_readsize_block;
+		kvec[2].iov_len	     = read_size_block_length;
+		data_to_send.length += (u32)kvec[2].iov_len;
+		data_to_send.kvec_num++;
+	}
+
+	hdr->length = (u16)data_to_send.length;
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+static void __mausb_process_in_isoch_short_resp(struct mausb_event *event,
+						struct ma_usb_hdr_common *hdr,
+						struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_short *data_block_hdr =
+			(struct ma_usb_hdr_isochdatablock_short *)
+			shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+				  opt_hdr_shift);
+	u8 *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers *
+				   sizeof(*data_block_hdr));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = urb_ctx->urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		dev_err(mausb_host_dev.this_device, "Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num  = data_block_hdr[i].segment_number;
+		u16 seg_size = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			dev_err(mausb_host_dev.this_device, "Too many segments: ep_handle=%#x, seg_num=%d, urb.number_of_packets=%d",
+				event->data.ep_handle, seg_num,
+				urb->number_of_packets);
+			break;
+		}
+
+		if (seg_size > urb->iso_frame_desc[seg_num].length) {
+			dev_err(mausb_host_dev.this_device, "Block to long for segment: ep_handle=%#x",
+				event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, seg_size) > end_of_packet) {
+			dev_err(mausb_host_dev.this_device, "End of segment after enf of packet: ep_handle=%#x",
+				event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset);
+		mausb_data_iterator_write(&urb_ctx->iterator, isoch_data,
+					  seg_size);
+
+		isoch_data = shift_ptr(isoch_data, seg_size);
+
+		urb->iso_frame_desc[seg_num].actual_length = seg_size;
+		urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+static void __mausb_process_in_isoch_std_resp(struct mausb_event *event,
+					      struct ma_usb_hdr_common *hdr,
+					      struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_std *data_block_hdr =
+		(struct ma_usb_hdr_isochdatablock_std *)
+		shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+			  opt_hdr_shift);
+	u8 *isoch_data =
+		shift_ptr(data_block_hdr, hdr->data.headers *
+			  sizeof(struct ma_usb_hdr_isochdatablock_std));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = (struct urb *)event->data.urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		dev_err(mausb_host_dev.this_device, "Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num   = data_block_hdr[i].segment_number;
+		u16 seg_len   = data_block_hdr[i].segment_length;
+		u16 block_len = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			dev_err(mausb_host_dev.this_device, "Too many segments: ep_handle=%#x, seg_num=%d, number_of_packets=%d",
+				event->data.ep_handle, seg_num,
+				urb->number_of_packets);
+			break;
+		}
+
+		if (block_len > urb->iso_frame_desc[seg_num].length -
+			     urb->iso_frame_desc[seg_num].actual_length) {
+			dev_err(mausb_host_dev.this_device, "Block too long for segment: ep_handle=%#x",
+				event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, block_len) >
+				       end_of_packet) {
+			dev_err(mausb_host_dev.this_device, "End of fragment after end of packet: ep_handle=%#x",
+				event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset +
+					 data_block_hdr[i].fragment_offset);
+		mausb_data_iterator_write(&urb_ctx->iterator,
+					  isoch_data, block_len);
+		isoch_data = shift_ptr(isoch_data, block_len);
+
+		urb->iso_frame_desc[seg_num].actual_length += block_len;
+
+		if (urb->iso_frame_desc[seg_num].actual_length == seg_len)
+			urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+void mausb_receive_isoch_in_data(struct mausb_device *dev,
+				 struct mausb_event *event,
+				 struct mausb_urb_ctx *urb_ctx)
+{
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	struct ma_usb_hdr_transfer *transfer_hdr =
+					mausb_get_data_transfer_hdr(common_hdr);
+
+	if (!(common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK)) {
+		/* Short ISO headers response */
+		__mausb_process_in_isoch_short_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_STD) {
+		/* Standard ISO headers response */
+		__mausb_process_in_isoch_std_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_LONG) {
+		/* Long ISO headers response */
+		dev_warn(mausb_host_dev.this_device, "Long isoc headers in response: ep_handle=%#x, req_id=%#x",
+			 event->data.ep_handle, transfer_hdr->req_id);
+	} else {
+		/* Error */
+		dev_err(mausb_host_dev.this_device, "Isoc header error in response: ep_handle=%#x, req_id=%#x",
+			event->data.ep_handle, transfer_hdr->req_id);
+	}
+}
+
+static inline u32
+__mausb_calculate_isoch_common_header_size(u32 num_of_segments)
+{
+	return MAUSB_ISOCH_TRANSFER_HDR_SIZE +
+			MAUSB_ISOCH_STANDARD_FORMAT_SIZE * num_of_segments;
+}
+
+static struct ma_usb_hdr_common *
+__mausb_create_isoch_out_transfer_packet(struct mausb_event *event,
+					 struct mausb_urb_ctx *urb_ctx,
+					 u16 payload_size, u32 seq_n,
+					 u32 start_of_segments,
+					 u32 number_of_segments)
+{
+	struct ma_usb_hdr_common		 *hdr;
+	struct ma_usb_hdr_isochtransfer		 *hdr_isochtransfer;
+	struct ma_usb_hdr_isochdatablock_std	 *isoc_header_std;
+	struct ma_usb_hdr_isochtransfer_optional *hdr_opt_isochtransfer;
+	struct urb *urb = (struct urb *)event->data.urb;
+	void *isoc_headers = NULL;
+	u32 length;
+	u16 i;
+	unsigned long block_length;
+	u32 number_of_packets = (u32)event->data.isoch_seg_num;
+	u32 size_of_request =
+		__mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr = kzalloc(size_of_request, GFP_KERNEL);
+	if (!hdr)
+		return NULL;
+
+	hdr->version	  = MA_USB_HDR_VERSION_1_0;
+	hdr->ssid	  = event->data.mausb_ssid;
+	hdr->flags	  = MA_USB_HDR_FLAGS_HOST;
+	hdr->dev_addr	  = event->data.mausb_address;
+	hdr->handle.epv	  = event->data.ep_handle;
+	hdr->data.status  = MA_USB_HDR_STATUS_NO_ERROR;
+	hdr->data.eps	  = MAUSB_TRANSFER_RESERVED;
+	hdr->data.t_flags = (u8)(usb_endpoint_type(&urb->ep->desc) << 3);
+
+	isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE);
+
+	for (i = (u16)start_of_segments;
+	     i < number_of_segments + start_of_segments; ++i) {
+		block_length = i < number_of_packets - 1 ?
+			urb->iso_frame_desc[i + 1].offset -
+			urb->iso_frame_desc[i].offset :
+			mausb_data_iterator_length(&urb_ctx->iterator) -
+			urb->iso_frame_desc[i].offset;
+
+		urb->iso_frame_desc[i].status = MA_USB_HDR_STATUS_UNSUCCESSFUL;
+		isoc_header_std = (struct ma_usb_hdr_isochdatablock_std *)
+			shift_ptr(isoc_headers,
+				  (u64)MAUSB_ISOCH_STANDARD_FORMAT_SIZE *
+				  (i - start_of_segments));
+		isoc_header_std->block_length	 = (u16)block_length;
+		isoc_header_std->segment_number  = i;
+		isoc_header_std->s_flags	 = 0;
+		isoc_header_std->segment_length  = (u16)block_length;
+		isoc_header_std->fragment_offset = 0;
+	}
+
+	length = __mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr->flags		|= MA_USB_HDR_FLAGS_TIMESTAMP;
+	hdr->type		 = (u8)MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER);
+	hdr->data.headers	 = (u16)number_of_segments;
+	hdr->data.i_flags	 = MA_USB_DATA_IFLAGS_HDR_FMT_STD |
+				      MA_USB_DATA_IFLAGS_ASAP;
+	hdr_opt_isochtransfer	    = mausb_hdr_isochtransfer_optional_hdr(hdr);
+	hdr_isochtransfer	    = mausb_get_isochtransfer_hdr(hdr);
+	hdr_isochtransfer->req_id   = event->data.req_id;
+	hdr_isochtransfer->seq_n    = seq_n;
+	hdr_isochtransfer->segments = number_of_packets;
+
+	hdr_isochtransfer->presentation_time = MA_USB_TRANSFER_RESERVED;
+
+	hdr_opt_isochtransfer->timestamp = MA_USB_TRANSFER_RESERVED;
+	hdr_opt_isochtransfer->mtd	 = MA_USB_TRANSFER_RESERVED;
+
+	hdr->length = (u16)length + payload_size;
+
+	return hdr;
+}
+
+static int
+mausb_init_isoch_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				  struct list_head *chunks_list,
+				  u32 *num_of_data_chunks,
+				  u32 num_of_packets)
+{
+	u32 header_size =
+		__mausb_calculate_isoch_common_header_size(num_of_packets);
+	int status = mausb_add_data_chunk(common_hdr, header_size, chunks_list);
+
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static
+int mausb_prepare_isoch_out_transfer_packet(struct ma_usb_hdr_common *hdr,
+					    struct mausb_event *event,
+					    struct mausb_urb_ctx *urb_ctx,
+					    struct mausb_kvec_data_wrapper *
+					    result_data_wrapper)
+{
+	u32 num_of_data_chunks	       = 0;
+	u32 num_of_payload_data_chunks = 0;
+	u32 segment_number	       = event->data.isoch_seg_num;
+	u32 payload_data_size;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	int status = 0;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_isoch_out_header_chunk(hdr, &chunks_list,
+					      &num_of_data_chunks,
+					      segment_number) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size = hdr->length -
+		__mausb_calculate_isoch_common_header_size(segment_number);
+
+	if (mausb_data_iterator_read(&urb_ctx->iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		dev_err(mausb_host_dev.this_device, "Data iterator read failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(result_data_wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		dev_err(mausb_host_dev.this_device, "Data wrapper init failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks:
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+static int mausb_create_and_send_isoch_transfer_req(struct mausb_device *dev,
+						    struct mausb_event *event,
+						    struct mausb_urb_ctx
+						    *urb_ctx, u32 *seq_n,
+						    u32 payload_size,
+						    u32 start_of_segments,
+						    u32 number_of_segments)
+{
+	struct ma_usb_hdr_common *hdr;
+	struct mausb_kvec_data_wrapper data_to_send;
+	int status;
+	enum mausb_channel channel;
+
+	hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx,
+						       (u16)payload_size,
+						       *seq_n,
+						       start_of_segments,
+						       number_of_segments);
+	if (!hdr) {
+		dev_alert(mausb_host_dev.this_device, "Isoch transfer packet alloc failed");
+		return -ENOMEM;
+	}
+	*seq_n = (*seq_n + 1) % (MA_USB_TRANSFER_SEQN_MAX + 1);
+
+	status = mausb_prepare_isoch_out_transfer_packet(hdr, event, urb_ctx,
+							 &data_to_send);
+	if (status < 0) {
+		dev_alert(mausb_host_dev.this_device, "Failed to prepare transfer packet");
+		kfree(hdr);
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data_to_send);
+
+	kfree(hdr);
+	kfree(data_to_send.kvec);
+
+	return status;
+}
+
+static inline int __mausb_send_isoch_out_packet(struct mausb_device *dev,
+						struct mausb_event *event,
+						struct mausb_urb_ctx *urb_ctx,
+						u32 *seq_n,
+						u32 *starting_segments,
+						u32 *rem_transfer_buf,
+						u32 *payload_size, u32 index)
+{
+	int status = mausb_create_and_send_isoch_transfer_req(dev, event,
+					urb_ctx, seq_n, *payload_size,
+					*starting_segments,
+					index - *starting_segments);
+	if (status < 0) {
+		dev_err(mausb_host_dev.this_device, "ISOCH transfer request create and send failed");
+		return status;
+	}
+	*starting_segments = index;
+	*rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	*payload_size	   = 0;
+
+	return 0;
+}
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx)
+{
+	u32   starting_segments = 0;
+	u32   rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	struct urb *urb = (struct urb *)mausb_event->data.urb;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+	u32 payload_size   = 0;
+	u32 chunk_size;
+	u32 seq_n	   = 0;
+	int status;
+	u32 i;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		if (i < number_of_packets - 1)
+			chunk_size = urb->iso_frame_desc[i + 1].offset -
+					urb->iso_frame_desc[i].offset;
+		else
+			chunk_size =
+				mausb_data_iterator_length(&urb_ctx->iterator) -
+						urb->iso_frame_desc[i].offset;
+
+		if (chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE >
+		    rem_transfer_buf) {
+			if (payload_size == 0) {
+				dev_warn(mausb_host_dev.this_device, "Fragmented");
+			} else {
+				status = __mausb_send_isoch_out_packet
+						(ma_dev, mausb_event, urb_ctx,
+						 &seq_n, &starting_segments,
+						 &rem_transfer_buf,
+						 &payload_size, i);
+				if (status < 0)
+					return status;
+				i--;
+				continue;
+			}
+		} else {
+			rem_transfer_buf -=
+				chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE;
+			payload_size += chunk_size;
+		}
+
+		if (i == number_of_packets - 1 || rem_transfer_buf == 0) {
+			status = __mausb_send_isoch_out_packet
+					(ma_dev, mausb_event, urb_ctx, &seq_n,
+					 &starting_segments, &rem_transfer_buf,
+					 &payload_size, i + 1);
+			if (status < 0)
+				return status;
+		}
+	}
+	return 0;
+}
+
+void mausb_receive_isoch_out(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	u16 i;
+
+	dev_vdbg(mausb_host_dev.this_device, "transfer_size=%d, rem_transfer_size=%d, status=%d",
+		 event->data.transfer_size, event->data.rem_transfer_size,
+		 event->status);
+
+	for (i = 0; i < urb->number_of_packets; ++i)
+		urb->iso_frame_desc[i].status = event->status;
+
+	mausb_complete_request(urb, event->data.payload_size, event->status);
+}
diff --git a/drivers/usb/host/mausb/hpal_data.h b/drivers/usb/host/mausb/hpal_data.h
new file mode 100644
index 000000000000..4b351d508966
--- /dev/null
+++ b/drivers/usb/host/mausb/hpal_data.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_DATA_H__
+#define __MAUSB_HPAL_DATA_H__
+
+#include <linux/types.h>
+
+#include "hpal_events.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event);
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+
+#define MAUSB_ISOCH_IN_KVEC_NUM 3
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev,
+			    struct mausb_event *event);
+void mausb_receive_isoch_in_data(struct mausb_device *dev,
+				 struct mausb_event *event,
+				 struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx);
+void mausb_receive_isoch_out(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_DATA_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 1/8] usb: Add MA-USB Host kernel module
  2020-05-15 12:34       ` [PATCH v6 1/8] usb: Add MA-USB Host kernel module Vladimir Stankovic
@ 2020-05-15 13:01         ` Greg KH
  2020-06-11 18:20           ` Vladimir Stankovic
  2020-05-15 13:02         ` Greg KH
  1 sibling, 1 reply; 62+ messages in thread
From: Greg KH @ 2020-05-15 13:01 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Fri, May 15, 2020 at 02:34:55PM +0200, Vladimir Stankovic wrote:
> --- /dev/null
> +++ b/drivers/usb/host/mausb/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Kernel configuration file for MA-USB Host driver.
> +#
> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> +#
> +
> +config USB_HOST_MAUSB
> +	tristate "Media Agnostic (MA) USB Host Driver"
> +	depends on USB=y

Why =y?  That should not be a requirement for any usb host driver.

> +	help
> +	  This is a Media Agnostic (MA) USB Host driver which enables host
> +	  communication via MA USB protocol stack.
> +
> +	  If this driver is compiled as a module, it will be named mausb_host.

Provide links to the userspace and spec here so that people have a
chance to be able to use this driver?



> diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
> new file mode 100644
> index 000000000000..cafccac0edba
> --- /dev/null
> +++ b/drivers/usb/host/mausb/Makefile
> @@ -0,0 +1,10 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Makefile for DisplayLink MA-USB Host driver.
> +#
> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> +#
> +
> +obj-$(CONFIG_USB_HOST_MAUSB) += mausb_host.o
> +mausb_host-y := mausb_core.o
> +mausb_host-y += utils.o
> diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
> new file mode 100644
> index 000000000000..44f76a1b74de
> --- /dev/null
> +++ b/drivers/usb/host/mausb/mausb_core.c
> @@ -0,0 +1,24 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> + */
> +#include <linux/module.h>
> +
> +#include "utils.h"
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("DisplayLink (UK) Ltd.");
> +
> +static int mausb_host_init(void)
> +{
> +	return mausb_host_dev_register();
> +}
> +
> +static void mausb_host_exit(void)
> +{
> +	dev_info(mausb_host_dev.this_device, "Module unloading started...");
> +	mausb_host_dev_deregister();
> +}
> +
> +module_init(mausb_host_init);
> +module_exit(mausb_host_exit);
> diff --git a/drivers/usb/host/mausb/utils.c b/drivers/usb/host/mausb/utils.c
> new file mode 100644
> index 000000000000..1cfa2140311e
> --- /dev/null
> +++ b/drivers/usb/host/mausb/utils.c
> @@ -0,0 +1,47 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> + */
> +#include "utils.h"
> +
> +#include <linux/fs.h>
> +#include <linux/slab.h>
> +
> +#define MAUSB_KERNEL_DEV_NAME "mausb_host"
> +#define MAUSB_READ_DEVICE_TIMEOUT_MS 500
> +
> +struct miscdevice mausb_host_dev;
> +
> +static int mausb_host_dev_open(struct inode *inode, struct file *filp)
> +{
> +	filp->private_data = NULL;
> +
> +	return 0;
> +}
> +
> +static int mausb_host_dev_release(struct inode *inode, struct file *filp)
> +{
> +	kfree(filp->private_data);
> +	filp->private_data = NULL;
> +
> +	return 0;
> +}
> +
> +static const struct file_operations mausb_host_dev_fops = {
> +	.open	 = mausb_host_dev_open,
> +	.release = mausb_host_dev_release,
> +};
> +
> +int mausb_host_dev_register(void)
> +{
> +	mausb_host_dev.minor = MISC_DYNAMIC_MINOR;
> +	mausb_host_dev.name = MAUSB_KERNEL_DEV_NAME;
> +	mausb_host_dev.fops = &mausb_host_dev_fops;
> +	mausb_host_dev.mode = 0;

You only have 1 device in the system at a time?  With a global
structure?  And no locking at all?

That feels _very_ wrong, why?

And mode of 0?  You don't want any userspace code to use this device
node?

confused,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 1/8] usb: Add MA-USB Host kernel module
  2020-05-15 12:34       ` [PATCH v6 1/8] usb: Add MA-USB Host kernel module Vladimir Stankovic
  2020-05-15 13:01         ` Greg KH
@ 2020-05-15 13:02         ` Greg KH
  2020-06-11 18:19           ` [External] " Vladimir Stankovic
  1 sibling, 1 reply; 62+ messages in thread
From: Greg KH @ 2020-05-15 13:02 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Fri, May 15, 2020 at 02:34:55PM +0200, Vladimir Stankovic wrote:
> Added utility macros, kernel device creation and cleanup, functions for
> handling log formatting and a placeholder module for MA-USB Host device
> driver.
> 
> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> ---
>  MAINTAINERS                         |  7 +++++
>  drivers/usb/Kconfig                 |  2 ++
>  drivers/usb/Makefile                |  1 +
>  drivers/usb/host/mausb/Kconfig      | 15 +++++++++
>  drivers/usb/host/mausb/Makefile     | 10 ++++++
>  drivers/usb/host/mausb/mausb_core.c | 24 +++++++++++++++
>  drivers/usb/host/mausb/utils.c      | 47 +++++++++++++++++++++++++++++
>  drivers/usb/host/mausb/utils.h      | 15 +++++++++
>  8 files changed, 121 insertions(+)
>  create mode 100644 drivers/usb/host/mausb/Kconfig
>  create mode 100644 drivers/usb/host/mausb/Makefile
>  create mode 100644 drivers/usb/host/mausb/mausb_core.c
>  create mode 100644 drivers/usb/host/mausb/utils.c
>  create mode 100644 drivers/usb/host/mausb/utils.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 091ec22c1a23..9b7b79215f47 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -10343,6 +10343,13 @@ W:	https://linuxtv.org
>  T:	git git://linuxtv.org/media_tree.git
>  F:	drivers/media/radio/radio-maxiradio*
>  
> +MEDIA AGNOSTIC (MA) USB HOST DRIVER
> +M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
> +L:	mausb-host-devel@displaylink.com

Please just do this on the linux-usb mailing list, why have a whole new
list for just a single small driver?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 3/8] usb: mausb_host: HCD initialization
  2020-05-15 12:34       ` [PATCH v6 3/8] usb: mausb_host: HCD initialization Vladimir Stankovic
@ 2020-05-15 13:03         ` Greg KH
  2020-06-11 18:19           ` Vladimir Stankovic
  2020-05-15 13:07         ` Greg KH
  1 sibling, 1 reply; 62+ messages in thread
From: Greg KH @ 2020-05-15 13:03 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Fri, May 15, 2020 at 02:34:57PM +0200, Vladimir Stankovic wrote:
> Implemented HCD initialization/deinitialization functionality.

Shortest changelog ever :(

Please describe things better.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver
  2020-04-30 20:02         ` Greg KH
@ 2020-05-15 13:04           ` Vladimir Stankovic
  2020-05-29 12:48             ` Pavel Machek
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-05-15 13:04 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

On 30.4.20. 22:02, Greg KH wrote:
> On Thu, Apr 30, 2020 at 06:51:10PM +0200, Vladimir Stankovic wrote:
>> On 28.4.20. 13:04, Greg KH wrote:
>>> On Sat, Apr 25, 2020 at 11:19:46AM +0200, vladimir.stankovic@displaylink.com wrote:
>>>> Media Agnostic (MA) USB Host driver provides USB connectivity over an
>>>> available network, allowing host device to access remote USB devices
>>>> attached to one or more MA USB devices (accessible via network).
>>>>
>>>> This driver has been developed to enable the host to communicate
>>>> with DisplayLink products supporting MA USB protocol (MA USB device,
>>>> in terms of MA USB Specification).
>>>>
>>>> MA USB protocol used by MA USB Host driver has been implemented in
>>>> accordance with MA USB Specification Release 1.0b.
>>>
>>> Is that a USB-released spec?
>> Correct, document is being maintained by USB IF and is publicly available.
>> However, I just noticed a typo, correct version is 1.0a. Will correct.
>>
>> In short, MA USB Specification defines an MA USB protocol that performs USB
>> communication via any communication medium. As such, it defines how to pack
>> USB data within MA USB payload, and how to communicate with remote MA USB device.
>>>
>>>>
>>>> This driver depends on the functions provided by DisplayLink's
>>>> user-space driver.
>>>
>>> Where can that userspace code be found?
>>>
>>> thanks,
>>>
>>> greg k-h
>>>
>> Userspace code is not publicly available. However, in short, it's purpose is
>> twofold, to provide interface to application layer, and to prepare MA USB packets
>> that will be used by remote device.
> 
> So you want us to take a one-off char-driver kernel code for a closed
> source userspace application for a public spec?  That feels really
> really odd, if not actually against a few licenses.  I hate to ask it,
> but are your lawyers ok with this?
> 
>> Related to userspace related questions (i.e. comments around two devices used),
>> we can provide detailed description of the used IPC. In that sense, please state
>> the most appropriate way/place to state/publish such description (i.e. is it ok
>> to add it within the cover letter, or publicly available URL is preferred). 
> 
> I asked a bunch of questions about this in the patches themselves, you
> all need to document the heck out of it everywhere you can, otherwise we
> can't even review the code properly.  Could you review it without
> knowing what userspace is supposed to be doing?
> 
> But, note, I will not take a spec-compliant driver that requires closed
> source userspace code, nor should you even want me to do that if you
> rely on Linux.
> 
> So please, release the userspace code, as it's going to have to be
> changed anyway as your current user/kernel api is broken/incorrect
> as-is.  Why not just bundle it in the kernel tree like we have the usbip
> code?  That way you know it all works properly, and better yet, it can
> be tested and maintained properly over time.
> 
> thanks,
> 
> greg k-h
> 

We've started internal discussion around user/kernel IPC.
Other comments from v5 have been addressed within v6.

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 3/8] usb: mausb_host: HCD initialization
  2020-05-15 12:34       ` [PATCH v6 3/8] usb: mausb_host: HCD initialization Vladimir Stankovic
  2020-05-15 13:03         ` Greg KH
@ 2020-05-15 13:07         ` Greg KH
  2020-06-11 18:18           ` [External] " Vladimir Stankovic
  1 sibling, 1 reply; 62+ messages in thread
From: Greg KH @ 2020-05-15 13:07 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Fri, May 15, 2020 at 02:34:57PM +0200, Vladimir Stankovic wrote:
> --- /dev/null
> +++ b/drivers/usb/host/mausb/hcd.c
> @@ -0,0 +1,120 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> + */
> +#include "hcd.h"
> +
> +#include "utils.h"
> +
> +static int mausb_bus_match(struct device *dev, struct device_driver *drv);
> +
> +static const struct file_operations mausb_fops;
> +
> +static unsigned int major;
> +static unsigned int minor = 1;
> +static dev_t devt;
> +static struct device *device;
> +
> +struct mausb_hcd	*mhcd;
> +static struct class	*mausb_class;
> +static struct bus_type	mausb_bus_type = {
> +	.name	= DEVICE_NAME,
> +	.match	= mausb_bus_match,
> +};

A static bus type???  For a single driver?

> +
> +static struct device_driver mausb_driver = {
> +	.name	= DEVICE_NAME,
> +	.bus	= &mausb_bus_type,
> +	.owner	= THIS_MODULE,
> +};

Wait, what???  A static "raw" struct device_driver?  Why???

> +
> +static void mausb_remove(void)
> +{
> +	struct usb_hcd *hcd, *shared_hcd;
> +
> +	hcd	   = mhcd->hcd_hs_ctx->hcd;
> +	shared_hcd = mhcd->hcd_ss_ctx->hcd;
> +
> +	if (shared_hcd) {
> +		usb_remove_hcd(shared_hcd);
> +		usb_put_hcd(shared_hcd);
> +		mhcd->hcd_ss_ctx = NULL;
> +	}
> +
> +	usb_remove_hcd(hcd);
> +	usb_put_hcd(hcd);
> +	mhcd->hcd_hs_ctx = NULL;
> +}
> +
> +static int mausb_bus_match(struct device *dev, struct device_driver *drv)
> +{
> +	if (strncmp(dev->bus->name, drv->name, strlen(drv->name)))
> +		return 0;
> +	else
> +		return 1;
> +}
> +
> +int mausb_init_hcd(void)
> +{
> +	int retval = register_chrdev(0, DEVICE_NAME, &mausb_fops);
> +
> +	if (retval < 0)
> +		return retval;
> +
> +	major = (unsigned int)retval;
> +	retval = bus_register(&mausb_bus_type);
> +	if (retval)
> +		goto bus_register_error;
> +
> +	mausb_class = class_create(THIS_MODULE, CLASS_NAME);
> +	if (IS_ERR(mausb_class)) {
> +		retval = (int)PTR_ERR(mausb_class);
> +		goto class_error;
> +	}
> +
> +	retval = driver_register(&mausb_driver);
> +	if (retval)
> +		goto driver_register_error;
> +
> +	mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC);
> +	if (!mhcd) {
> +		retval = -ENOMEM;
> +		goto mausb_hcd_alloc_failed;
> +	}
> +
> +	devt = MKDEV(major, minor);
> +	device = device_create(mausb_class, NULL, devt, mhcd, DEVICE_NAME);
> +	if (IS_ERR(device)) {
> +		retval = (int)IS_ERR(device);
> +		goto device_create_error;
> +	}
> +
> +	device->driver = &mausb_driver;

Why?  What is this device going to do?  What do you need it for?

> +
> +	return retval;
> +device_create_error:
> +	kfree(mhcd);
> +	mhcd = NULL;
> +mausb_hcd_alloc_failed:
> +	driver_unregister(&mausb_driver);
> +driver_register_error:
> +	class_destroy(mausb_class);
> +class_error:
> +	bus_unregister(&mausb_bus_type);
> +bus_register_error:
> +	unregister_chrdev(major, DEVICE_NAME);

I thought you were using the misc device, what are you doing with a
class and chardev?  Why is that still here?




> +
> +	return retval;
> +}
> +
> +void mausb_deinit_hcd(void)
> +{
> +	if (mhcd) {
> +		mausb_remove();
> +		device_destroy(mausb_class, devt);
> +		driver_unregister(&mausb_driver);
> +		class_destroy(mausb_class);
> +		bus_unregister(&mausb_bus_type);
> +		unregister_chrdev(major, DEVICE_NAME);
> +	}
> +}
> diff --git a/drivers/usb/host/mausb/hcd.h b/drivers/usb/host/mausb/hcd.h
> new file mode 100644
> index 000000000000..e2374af67663
> --- /dev/null
> +++ b/drivers/usb/host/mausb/hcd.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> + */
> +#ifndef __MAUSB_HCD_H__
> +#define __MAUSB_HCD_H__
> +
> +#include <linux/slab.h>
> +#include <linux/usb.h>
> +#include <linux/usb/hcd.h>
> +
> +#define DEVICE_NAME "mausb_host_hcd"
> +#define CLASS_NAME "mausb"
> +
> +#define NUMBER_OF_PORTS		15

Why this max?

> +
> +#define MAX_USB_DEVICE_DEPTH	6

Where does this max come from?

> +
> +#define RESPONSE_TIMEOUT	5000

Units?

> +
> +#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
> +#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400

Device ids?  Something else?

If something else, use BIT()?

> +
> +enum mausb_device_type {
> +	USBDEVICE = 0,
> +	USB20HUB  = 1,
> +	USB30HUB  = 2

Trailing , please

> +};
> +
> +enum mausb_device_speed {
> +	LOW_SPEED	 = 0,
> +	FULL_SPEED	 = 1,
> +	HIGH_SPEED	 = 2,
> +	SUPER_SPEED	 = 3,
> +	SUPER_SPEED_PLUS = 4

, please.

> +};

Where do these values come from?

> +
> +struct mausb_hcd {
> +	spinlock_t	lock;	/* Protect HCD during URB processing */
> +	struct device	*pdev;
> +	u16		connected_ports;

Why u16?

> +
> +	struct rb_root	mausb_urbs;
> +	struct hub_ctx	*hcd_ss_ctx;
> +	struct hub_ctx	*hcd_hs_ctx;
> +	struct notifier_block power_state_listener;
> +};
> +
> +struct mausb_dev {
> +	u32		port_status;
> +	struct rb_root	usb_devices;
> +	u8		dev_speed;
> +	void		*ma_dev;
> +};
> +
> +struct hub_ctx {
> +	struct mausb_hcd *mhcd;
> +	struct usb_hcd	 *hcd;
> +	struct mausb_dev ma_devs[NUMBER_OF_PORTS];
> +};
> +
> +int mausb_init_hcd(void);
> +void mausb_deinit_hcd(void);
> +
> +#endif /* __MAUSB_HCD_H__ */
> diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
> index 44f76a1b74de..485f241d2b4c 100644
> --- a/drivers/usb/host/mausb/mausb_core.c
> +++ b/drivers/usb/host/mausb/mausb_core.c
> @@ -4,6 +4,7 @@
>   */
>  #include <linux/module.h>
>  
> +#include "hcd.h"
>  #include "utils.h"
>  
>  MODULE_LICENSE("GPL");
> @@ -11,12 +12,27 @@ MODULE_AUTHOR("DisplayLink (UK) Ltd.");
>  
>  static int mausb_host_init(void)
>  {
> -	return mausb_host_dev_register();
> +	int status = mausb_host_dev_register();
> +
> +	if (status < 0)
> +		goto exit;
> +
> +	status = mausb_init_hcd();
> +	if (status < 0)
> +		goto cleanup_dev;
> +
> +	return 0;
> +
> +cleanup_dev:
> +	mausb_host_dev_deregister();
> +exit:
> +	return status;
>  }
>  
>  static void mausb_host_exit(void)
>  {
>  	dev_info(mausb_host_dev.this_device, "Module unloading started...");

This is debugging statements, please remove.  If a driver works
properly, it does not print anything out.

Especially as you never give the user the chance to see if module
unloading ever finished :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 0/8] Add MA USB Host driver
  2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
                         ` (7 preceding siblings ...)
  2020-05-15 12:35       ` [PATCH v6 8/8] usb: mausb_host: Process MA-USB data packets Vladimir Stankovic
@ 2020-05-15 13:08       ` Greg KH
  8 siblings, 0 replies; 62+ messages in thread
From: Greg KH @ 2020-05-15 13:08 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Fri, May 15, 2020 at 02:34:54PM +0200, Vladimir Stankovic wrote:
> Media Agnostic (MA) USB Host driver provides USB connectivity over an
> available network, allowing host device to access remote USB devices
> attached to one or more MA USB devices (accessible via network).
> 
> This driver has been developed to enable the host to communicate
> with DisplayLink products supporting MA USB protocol (MA USB device,
> in terms of MA USB Specification).
> 
> MA USB protocol used by MA USB Host driver has been implemented in
> accordance with MA USB Specification Release 1.0b.
> 
> This driver depends on the functions provided by DisplayLink's
> user-space driver.

Any pointer to the code for this?  Without reviewing that, reviewing
this code is going to be impossible, as I stated last time.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver
  2020-05-15 13:04           ` Vladimir Stankovic
@ 2020-05-29 12:48             ` Pavel Machek
  0 siblings, 0 replies; 62+ messages in thread
From: Pavel Machek @ 2020-05-29 12:48 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: Greg KH, linux-kernel, linux-usb, mausb-host-devel

Hi!

> >>>> MA USB protocol used by MA USB Host driver has been implemented in
> >>>> accordance with MA USB Specification Release 1.0b.
> >>>
> >>> Is that a USB-released spec?
> >> Correct, document is being maintained by USB IF and is publicly available.
> >> However, I just noticed a typo, correct version is 1.0a. Will correct.
> >>
> >> In short, MA USB Specification defines an MA USB protocol that performs USB
> >> communication via any communication medium. As such, it defines how to pack
> >> USB data within MA USB payload, and how to communicate with remote MA USB device.

Ok

> >> Userspace code is not publicly available. However, in short, it's purpose is
> >> twofold, to provide interface to application layer, and to prepare MA USB packets
> >> that will be used by remote device.
> > 
> > So you want us to take a one-off char-driver kernel code for a closed
> > source userspace application for a public spec?  That feels really
> > really odd, if not actually against a few licenses.  I hate to ask it,
> > but are your lawyers ok with this?

More importantly... does that work?

Userland is okay for communication setup, but if userspace is involved with every packet
being sent... It will deadlock.

One example: attach mass storage device over MUSB, put swap there; what happens if your
userland helper is now swapped out?

									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v6 3/8] usb: mausb_host: HCD initialization
  2020-05-15 13:07         ` Greg KH
@ 2020-06-11 18:18           ` Vladimir Stankovic
  2020-06-18  8:18             ` Greg KH
  0 siblings, 1 reply; 62+ messages in thread
From: Vladimir Stankovic @ 2020-06-11 18:18 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

On 15.5.20. 15:07, Greg KH wrote:
> On Fri, May 15, 2020 at 02:34:57PM +0200, Vladimir Stankovic wrote:
>> --- /dev/null
>> +++ b/drivers/usb/host/mausb/hcd.c
>> @@ -0,0 +1,120 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
>> + */
>> +#include "hcd.h"
>> +
>> +#include "utils.h"
>> +
>> +static int mausb_bus_match(struct device *dev, struct device_driver *drv);
>> +
>> +static const struct file_operations mausb_fops;
>> +
>> +static unsigned int major;
>> +static unsigned int minor = 1;
>> +static dev_t devt;
>> +static struct device *device;
>> +
>> +struct mausb_hcd	*mhcd;
>> +static struct class	*mausb_class;
>> +static struct bus_type	mausb_bus_type = {
>> +	.name	= DEVICE_NAME,
>> +	.match	= mausb_bus_match,
>> +};
> 
> A static bus type???  For a single driver?
> 
>> +
>> +static struct device_driver mausb_driver = {
>> +	.name	= DEVICE_NAME,
>> +	.bus	= &mausb_bus_type,
>> +	.owner	= THIS_MODULE,
>> +};
> 
> Wait, what???  A static "raw" struct device_driver?  Why???
This was our initial driver setup that was "inherited" from some
in-tree drivers. We are currently revising driver setup. In general,
device driver will not work properly w/o bus being setup and the
only way to avoid explicit bus and simplify driver setup is to use
platform driver; however, we are not aware of any explicit dependency
on the platform, so not sure whether it's acceptable to switch to
platform device driver setup.
> 
>> +
>> +static void mausb_remove(void)
>> +{
>> +	struct usb_hcd *hcd, *shared_hcd;
>> +
>> +	hcd	   = mhcd->hcd_hs_ctx->hcd;
>> +	shared_hcd = mhcd->hcd_ss_ctx->hcd;
>> +
>> +	if (shared_hcd) {
>> +		usb_remove_hcd(shared_hcd);
>> +		usb_put_hcd(shared_hcd);
>> +		mhcd->hcd_ss_ctx = NULL;
>> +	}
>> +
>> +	usb_remove_hcd(hcd);
>> +	usb_put_hcd(hcd);
>> +	mhcd->hcd_hs_ctx = NULL;
>> +}
>> +
>> +static int mausb_bus_match(struct device *dev, struct device_driver *drv)
>> +{
>> +	if (strncmp(dev->bus->name, drv->name, strlen(drv->name)))
>> +		return 0;
>> +	else
>> +		return 1;
>> +}
>> +
>> +int mausb_init_hcd(void)
>> +{
>> +	int retval = register_chrdev(0, DEVICE_NAME, &mausb_fops);
>> +
>> +	if (retval < 0)
>> +		return retval;
>> +
>> +	major = (unsigned int)retval;
>> +	retval = bus_register(&mausb_bus_type);
>> +	if (retval)
>> +		goto bus_register_error;
>> +
>> +	mausb_class = class_create(THIS_MODULE, CLASS_NAME);
>> +	if (IS_ERR(mausb_class)) {
>> +		retval = (int)PTR_ERR(mausb_class);
>> +		goto class_error;
>> +	}
>> +
>> +	retval = driver_register(&mausb_driver);
>> +	if (retval)
>> +		goto driver_register_error;
>> +
>> +	mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC);
>> +	if (!mhcd) {
>> +		retval = -ENOMEM;
>> +		goto mausb_hcd_alloc_failed;
>> +	}
>> +
>> +	devt = MKDEV(major, minor);
>> +	device = device_create(mausb_class, NULL, devt, mhcd, DEVICE_NAME);
>> +	if (IS_ERR(device)) {
>> +		retval = (int)IS_ERR(device);
>> +		goto device_create_error;
>> +	}
>> +
>> +	device->driver = &mausb_driver;
> 
> Why?  What is this device going to do?  What do you need it for?
Yeah, this one is not needed at all. Will be removed.
> 
>> +
>> +	return retval;
>> +device_create_error:
>> +	kfree(mhcd);
>> +	mhcd = NULL;
>> +mausb_hcd_alloc_failed:
>> +	driver_unregister(&mausb_driver);
>> +driver_register_error:
>> +	class_destroy(mausb_class);
>> +class_error:
>> +	bus_unregister(&mausb_bus_type);
>> +bus_register_error:
>> +	unregister_chrdev(major, DEVICE_NAME);
> 
> I thought you were using the misc device, what are you doing with a
> class and chardev?  Why is that still here?
> 
We're currently revising driver setup logic - this part should be much
cleaner in new patch version.
> 
> 
> 
>> +
>> +	return retval;
>> +}
>> +
>> +void mausb_deinit_hcd(void)
>> +{
>> +	if (mhcd) {
>> +		mausb_remove();
>> +		device_destroy(mausb_class, devt);
>> +		driver_unregister(&mausb_driver);
>> +		class_destroy(mausb_class);
>> +		bus_unregister(&mausb_bus_type);
>> +		unregister_chrdev(major, DEVICE_NAME);
>> +	}
>> +}
>> diff --git a/drivers/usb/host/mausb/hcd.h b/drivers/usb/host/mausb/hcd.h
>> new file mode 100644
>> index 000000000000..e2374af67663
>> --- /dev/null
>> +++ b/drivers/usb/host/mausb/hcd.h
>> @@ -0,0 +1,65 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
>> + */
>> +#ifndef __MAUSB_HCD_H__
>> +#define __MAUSB_HCD_H__
>> +
>> +#include <linux/slab.h>
>> +#include <linux/usb.h>
>> +#include <linux/usb/hcd.h>
>> +
>> +#define DEVICE_NAME "mausb_host_hcd"
>> +#define CLASS_NAME "mausb"
>> +
>> +#define NUMBER_OF_PORTS		15
> 
> Why this max?
Related to USB_SS_MAXPORTS - a worst case scenario.
> 
>> +
>> +#define MAX_USB_DEVICE_DEPTH	6
> 
> Where does this max come from?
Unknown origin and unused - will be removed.
> 
>> +
>> +#define RESPONSE_TIMEOUT	5000
> 
> Units?
ms - will be renamed to reflect the same.
> 
>> +
>> +#define MAUSB_PORT_20_STATUS_LOW_SPEED       0x0200
>> +#define MAUSB_PORT_20_STATUS_HIGH_SPEED      0x0400
> 
> Device ids?  Something else?
> 
> If something else, use BIT()?
> 
>> +
>> +enum mausb_device_type {
>> +	USBDEVICE = 0,
>> +	USB20HUB  = 1,
>> +	USB30HUB  = 2
> 
> Trailing , please
> 
>> +};
>> +
>> +enum mausb_device_speed {
>> +	LOW_SPEED	 = 0,
>> +	FULL_SPEED	 = 1,
>> +	HIGH_SPEED	 = 2,
>> +	SUPER_SPEED	 = 3,
>> +	SUPER_SPEED_PLUS = 4
> 
> , please.
> 
>> +};
> 
> Where do these values come from?
> 
>> +
>> +struct mausb_hcd {
>> +	spinlock_t	lock;	/* Protect HCD during URB processing */
>> +	struct device	*pdev;
>> +	u16		connected_ports;
> 
> Why u16?
Seems odd, since max number of ports is much less. Should be corrected.
> 
>> +
>> +	struct rb_root	mausb_urbs;
>> +	struct hub_ctx	*hcd_ss_ctx;
>> +	struct hub_ctx	*hcd_hs_ctx;
>> +	struct notifier_block power_state_listener;
>> +};
>> +
>> +struct mausb_dev {
>> +	u32		port_status;
>> +	struct rb_root	usb_devices;
>> +	u8		dev_speed;
>> +	void		*ma_dev;
>> +};
>> +
>> +struct hub_ctx {
>> +	struct mausb_hcd *mhcd;
>> +	struct usb_hcd	 *hcd;
>> +	struct mausb_dev ma_devs[NUMBER_OF_PORTS];
>> +};
>> +
>> +int mausb_init_hcd(void);
>> +void mausb_deinit_hcd(void);
>> +
>> +#endif /* __MAUSB_HCD_H__ */
>> diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
>> index 44f76a1b74de..485f241d2b4c 100644
>> --- a/drivers/usb/host/mausb/mausb_core.c
>> +++ b/drivers/usb/host/mausb/mausb_core.c
>> @@ -4,6 +4,7 @@
>>   */
>>  #include <linux/module.h>
>>  
>> +#include "hcd.h"
>>  #include "utils.h"
>>  
>>  MODULE_LICENSE("GPL");
>> @@ -11,12 +12,27 @@ MODULE_AUTHOR("DisplayLink (UK) Ltd.");
>>  
>>  static int mausb_host_init(void)
>>  {
>> -	return mausb_host_dev_register();
>> +	int status = mausb_host_dev_register();
>> +
>> +	if (status < 0)
>> +		goto exit;
>> +
>> +	status = mausb_init_hcd();
>> +	if (status < 0)
>> +		goto cleanup_dev;
>> +
>> +	return 0;
>> +
>> +cleanup_dev:
>> +	mausb_host_dev_deregister();
>> +exit:
>> +	return status;
>>  }
>>  
>>  static void mausb_host_exit(void)
>>  {
>>  	dev_info(mausb_host_dev.this_device, "Module unloading started...");
> 
> This is debugging statements, please remove.  If a driver works
> properly, it does not print anything out.
> 
> Especially as you never give the user the chance to see if module
> unloading ever finished :)
Agree :)
> 
> thanks,
> 
> greg k-h
> 


-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 3/8] usb: mausb_host: HCD initialization
  2020-05-15 13:03         ` Greg KH
@ 2020-06-11 18:19           ` Vladimir Stankovic
  0 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-06-11 18:19 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

On 15.5.20. 15:03, Greg KH wrote:
> On Fri, May 15, 2020 at 02:34:57PM +0200, Vladimir Stankovic wrote:
>> Implemented HCD initialization/deinitialization functionality.
> 
> Shortest changelog ever :(
> 
> Please describe things better.
> 
> thanks,
> 
> greg k-h
> 
Agree. We'll start adding a bunch of documentation and comments in
following patch versions - this was a quick fix during patchset reorg done
in initial phase of upstreaming.

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v6 1/8] usb: Add MA-USB Host kernel module
  2020-05-15 13:02         ` Greg KH
@ 2020-06-11 18:19           ` Vladimir Stankovic
  0 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-06-11 18:19 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

On 15.5.20. 15:02, Greg KH wrote:
> On Fri, May 15, 2020 at 02:34:55PM +0200, Vladimir Stankovic wrote:
>> Added utility macros, kernel device creation and cleanup, functions for
>> handling log formatting and a placeholder module for MA-USB Host device
>> driver.
>>
>> Signed-off-by: Vladimir Stankovic <vladimir.stankovic@displaylink.com>
>> ---
>>  MAINTAINERS                         |  7 +++++
>>  drivers/usb/Kconfig                 |  2 ++
>>  drivers/usb/Makefile                |  1 +
>>  drivers/usb/host/mausb/Kconfig      | 15 +++++++++
>>  drivers/usb/host/mausb/Makefile     | 10 ++++++
>>  drivers/usb/host/mausb/mausb_core.c | 24 +++++++++++++++
>>  drivers/usb/host/mausb/utils.c      | 47 +++++++++++++++++++++++++++++
>>  drivers/usb/host/mausb/utils.h      | 15 +++++++++
>>  8 files changed, 121 insertions(+)
>>  create mode 100644 drivers/usb/host/mausb/Kconfig
>>  create mode 100644 drivers/usb/host/mausb/Makefile
>>  create mode 100644 drivers/usb/host/mausb/mausb_core.c
>>  create mode 100644 drivers/usb/host/mausb/utils.c
>>  create mode 100644 drivers/usb/host/mausb/utils.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 091ec22c1a23..9b7b79215f47 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -10343,6 +10343,13 @@ W:	https://linuxtv.org
>>  T:	git git://linuxtv.org/media_tree.git
>>  F:	drivers/media/radio/radio-maxiradio*
>>  
>> +MEDIA AGNOSTIC (MA) USB HOST DRIVER
>> +M:	Vladimir Stankovic <vladimir.stankovic@displaylink.com>
>> +L:	mausb-host-devel@displaylink.com
> 
> Please just do this on the linux-usb mailing list, why have a whole new
> list for just a single small driver?
> 
> thanks,
> 
> greg k-h
> 
Idea was to have the team notified about any comment/issue within the
driver. However, as you noted, same can be achieved with linux-usb
subscriptions.

-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH v6 1/8] usb: Add MA-USB Host kernel module
  2020-05-15 13:01         ` Greg KH
@ 2020-06-11 18:20           ` Vladimir Stankovic
  0 siblings, 0 replies; 62+ messages in thread
From: Vladimir Stankovic @ 2020-06-11 18:20 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, linux-usb, mausb-host-devel

On 15.5.20. 15:01, Greg KH wrote:
> On Fri, May 15, 2020 at 02:34:55PM +0200, Vladimir Stankovic wrote:
>> --- /dev/null
>> +++ b/drivers/usb/host/mausb/Kconfig
>> @@ -0,0 +1,15 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Kernel configuration file for MA-USB Host driver.
>> +#
>> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
>> +#
>> +
>> +config USB_HOST_MAUSB
>> +	tristate "Media Agnostic (MA) USB Host Driver"
>> +	depends on USB=y
> 
> Why =y?  That should not be a requirement for any usb host driver.
With current driver in-tree location, it's not needed. Will be removed.
> 
>> +	help
>> +	  This is a Media Agnostic (MA) USB Host driver which enables host
>> +	  communication via MA USB protocol stack.
>> +
>> +	  If this driver is compiled as a module, it will be named mausb_host.
> 
> Provide links to the userspace and spec here so that people have a
> chance to be able to use this driver?
> 
> 
> 
>> diff --git a/drivers/usb/host/mausb/Makefile b/drivers/usb/host/mausb/Makefile
>> new file mode 100644
>> index 000000000000..cafccac0edba
>> --- /dev/null
>> +++ b/drivers/usb/host/mausb/Makefile
>> @@ -0,0 +1,10 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +#
>> +# Makefile for DisplayLink MA-USB Host driver.
>> +#
>> +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
>> +#
>> +
>> +obj-$(CONFIG_USB_HOST_MAUSB) += mausb_host.o
>> +mausb_host-y := mausb_core.o
>> +mausb_host-y += utils.o
>> diff --git a/drivers/usb/host/mausb/mausb_core.c b/drivers/usb/host/mausb/mausb_core.c
>> new file mode 100644
>> index 000000000000..44f76a1b74de
>> --- /dev/null
>> +++ b/drivers/usb/host/mausb/mausb_core.c
>> @@ -0,0 +1,24 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
>> + */
>> +#include <linux/module.h>
>> +
>> +#include "utils.h"
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("DisplayLink (UK) Ltd.");
>> +
>> +static int mausb_host_init(void)
>> +{
>> +	return mausb_host_dev_register();
>> +}
>> +
>> +static void mausb_host_exit(void)
>> +{
>> +	dev_info(mausb_host_dev.this_device, "Module unloading started...");
>> +	mausb_host_dev_deregister();
>> +}
>> +
>> +module_init(mausb_host_init);
>> +module_exit(mausb_host_exit);
>> diff --git a/drivers/usb/host/mausb/utils.c b/drivers/usb/host/mausb/utils.c
>> new file mode 100644
>> index 000000000000..1cfa2140311e
>> --- /dev/null
>> +++ b/drivers/usb/host/mausb/utils.c
>> @@ -0,0 +1,47 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
>> + */
>> +#include "utils.h"
>> +
>> +#include <linux/fs.h>
>> +#include <linux/slab.h>
>> +
>> +#define MAUSB_KERNEL_DEV_NAME "mausb_host"
>> +#define MAUSB_READ_DEVICE_TIMEOUT_MS 500
>> +
>> +struct miscdevice mausb_host_dev;
>> +
>> +static int mausb_host_dev_open(struct inode *inode, struct file *filp)
>> +{
>> +	filp->private_data = NULL;
>> +
>> +	return 0;
>> +}
>> +
>> +static int mausb_host_dev_release(struct inode *inode, struct file *filp)
>> +{
>> +	kfree(filp->private_data);
>> +	filp->private_data = NULL;
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct file_operations mausb_host_dev_fops = {
>> +	.open	 = mausb_host_dev_open,
>> +	.release = mausb_host_dev_release,
>> +};
>> +
>> +int mausb_host_dev_register(void)
>> +{
>> +	mausb_host_dev.minor = MISC_DYNAMIC_MINOR;
>> +	mausb_host_dev.name = MAUSB_KERNEL_DEV_NAME;
>> +	mausb_host_dev.fops = &mausb_host_dev_fops;
>> +	mausb_host_dev.mode = 0;
> 
> You only have 1 device in the system at a time?  With a global
> structure?  And no locking at all?
> 
> That feels _very_ wrong, why?
> 
> And mode of 0?  You don't want any userspace code to use this device
> node?
> 
> confused,
> 
> greg k-h
> 
Agree. This will be revised in following patch version.


-- 
Regards,
Vladimir.


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [External] Re: [PATCH v6 3/8] usb: mausb_host: HCD initialization
  2020-06-11 18:18           ` [External] " Vladimir Stankovic
@ 2020-06-18  8:18             ` Greg KH
  0 siblings, 0 replies; 62+ messages in thread
From: Greg KH @ 2020-06-18  8:18 UTC (permalink / raw)
  To: Vladimir Stankovic; +Cc: linux-kernel, linux-usb, mausb-host-devel

On Thu, Jun 11, 2020 at 08:18:34PM +0200, Vladimir Stankovic wrote:
> On 15.5.20. 15:07, Greg KH wrote:
> > On Fri, May 15, 2020 at 02:34:57PM +0200, Vladimir Stankovic wrote:
> >> --- /dev/null
> >> +++ b/drivers/usb/host/mausb/hcd.c
> >> @@ -0,0 +1,120 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
> >> + */
> >> +#include "hcd.h"
> >> +
> >> +#include "utils.h"
> >> +
> >> +static int mausb_bus_match(struct device *dev, struct device_driver *drv);
> >> +
> >> +static const struct file_operations mausb_fops;
> >> +
> >> +static unsigned int major;
> >> +static unsigned int minor = 1;
> >> +static dev_t devt;
> >> +static struct device *device;
> >> +
> >> +struct mausb_hcd	*mhcd;
> >> +static struct class	*mausb_class;
> >> +static struct bus_type	mausb_bus_type = {
> >> +	.name	= DEVICE_NAME,
> >> +	.match	= mausb_bus_match,
> >> +};
> > 
> > A static bus type???  For a single driver?
> > 
> >> +
> >> +static struct device_driver mausb_driver = {
> >> +	.name	= DEVICE_NAME,
> >> +	.bus	= &mausb_bus_type,
> >> +	.owner	= THIS_MODULE,
> >> +};
> > 
> > Wait, what???  A static "raw" struct device_driver?  Why???
> This was our initial driver setup that was "inherited" from some
> in-tree drivers. We are currently revising driver setup. In general,
> device driver will not work properly w/o bus being setup and the
> only way to avoid explicit bus and simplify driver setup is to use
> platform driver; however, we are not aware of any explicit dependency
> on the platform, so not sure whether it's acceptable to switch to
> platform device driver setup.

platform devices/drivers are to be used ONLY if your really have a
platform device (i.e. something connected to the system only through
device-tree or hard-coded memory locations.)  Do not use this for
virtual devices or anything else that is on a "real" bus.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2020-06-18  8:19 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-12 14:42 [PATCH v3 0/8] Add MA USB Host driver Vladimir Stankovic
2020-03-27 15:26 ` [PATCH v4 " vladimir.stankovic
2020-03-27 15:26   ` [PATCH v4 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
2020-03-27 16:25     ` Alan Stern
2020-03-27 15:26   ` [PATCH v4 2/8] usb: mausb_host: Add link layer implementation vladimir.stankovic
2020-03-27 15:26   ` [PATCH v4 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
2020-03-27 15:26   ` [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
2020-03-27 16:37     ` Alan Stern
2020-04-13 15:16       ` Vladimir Stankovic
2020-03-27 15:26   ` [PATCH v4 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
2020-03-27 16:35     ` Alan Stern
2020-03-28  3:56     ` kbuild test robot
2020-03-27 15:26   ` [PATCH v4 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication vladimir.stankovic
2020-03-27 15:26   ` [PATCH v4 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
2020-03-28 10:35     ` kbuild test robot
2020-04-04 16:07     ` kbuild test robot
2020-03-27 15:26   ` [PATCH v4 8/8] usb: mausb_host: Process MA-USB data packets vladimir.stankovic
2020-04-25  9:19   ` [PATCH v5 0/8] Add MA USB Host driver vladimir.stankovic
2020-04-25  9:19     ` [PATCH v5 1/8] usb: Add MA-USB Host kernel module vladimir.stankovic
2020-04-28 11:03       ` Greg KH
2020-04-25  9:19     ` [PATCH v5 2/8] usb: mausb_host: Add link layer implementation vladimir.stankovic
2020-04-25  9:19     ` [PATCH v5 3/8] usb: mausb_host: HCD initialization vladimir.stankovic
2020-04-28 11:07       ` Greg KH
2020-04-25  9:19     ` [PATCH v5 4/8] usb: mausb_host: Implement initial hub handlers vladimir.stankovic
2020-04-25  9:19     ` [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing vladimir.stankovic
2020-04-26  0:32       ` Alan Stern
2020-04-26 12:32         ` Vladimir Stankovic
2020-04-26 14:31           ` Alan Stern
2020-04-26 14:45             ` [External] " Vladimir Stankovic
2020-04-26 20:56               ` Alan Stern
2020-04-30 14:37                 ` Vladimir Stankovic
2020-04-30 15:18                   ` Alan Stern
2020-04-30 15:34                     ` Vladimir Stankovic
2020-04-30 15:41                       ` Alan Stern
2020-04-25  9:19     ` [PATCH v5 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication vladimir.stankovic
2020-04-25  9:19     ` [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing vladimir.stankovic
2020-04-28 11:08       ` Greg KH
2020-04-25  9:19     ` [PATCH v5 8/8] usb: mausb_host: Process MA-USB data packets vladimir.stankovic
2020-04-28 11:04     ` [PATCH v5 0/8] Add MA USB Host driver Greg KH
2020-04-30 16:51       ` [External] " Vladimir Stankovic
2020-04-30 20:02         ` Greg KH
2020-05-15 13:04           ` Vladimir Stankovic
2020-05-29 12:48             ` Pavel Machek
2020-05-15 12:34     ` [PATCH v6 " Vladimir Stankovic
2020-05-15 12:34       ` [PATCH v6 1/8] usb: Add MA-USB Host kernel module Vladimir Stankovic
2020-05-15 13:01         ` Greg KH
2020-06-11 18:20           ` Vladimir Stankovic
2020-05-15 13:02         ` Greg KH
2020-06-11 18:19           ` [External] " Vladimir Stankovic
2020-05-15 12:34       ` [PATCH v6 2/8] usb: mausb_host: Add link layer implementation Vladimir Stankovic
2020-05-15 12:34       ` [PATCH v6 3/8] usb: mausb_host: HCD initialization Vladimir Stankovic
2020-05-15 13:03         ` Greg KH
2020-06-11 18:19           ` Vladimir Stankovic
2020-05-15 13:07         ` Greg KH
2020-06-11 18:18           ` [External] " Vladimir Stankovic
2020-06-18  8:18             ` Greg KH
2020-05-15 12:34       ` [PATCH v6 4/8] usb: mausb_host: Implement initial hub handlers Vladimir Stankovic
2020-05-15 12:34       ` [PATCH v6 5/8] usb: mausb_host: Introduce PAL processing Vladimir Stankovic
2020-05-15 12:35       ` [PATCH v6 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication Vladimir Stankovic
2020-05-15 12:35       ` [PATCH v6 7/8] usb: mausb_host: MA-USB PAL events processing Vladimir Stankovic
2020-05-15 12:35       ` [PATCH v6 8/8] usb: mausb_host: Process MA-USB data packets Vladimir Stankovic
2020-05-15 13:08       ` [PATCH v6 0/8] Add MA USB Host driver Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).