2020-03-12 14:43:14

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v3 0/8] Add MA USB Host driver

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


2020-03-27 15:28:45

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v4 0/8] Add MA USB Host driver

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

2020-03-27 15:29:16

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v4 3/8] usb: mausb_host: HCD initialization

Implemented HCD initialization/deinitialization functionality.

Signed-off-by: Vladimir Stankovic <[email protected]>
---
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

2020-04-25 09:22:25

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 0/8] Add MA USB Host driver

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

2020-04-25 09:22:28

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 6/8] usb: mausb_host: Add logic for PAL-to-PAL communication

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 <[email protected]>
---
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

2020-04-25 09:22:49

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing

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 <[email protected]>
---
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

2020-04-25 09:22:44

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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

Signed-off-by: Vladimir Stankovic <[email protected]>
---
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

2020-04-25 09:24:28

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 1/8] usb: Add MA-USB Host kernel module

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 <[email protected]>
---
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 <[email protected]>
+L: [email protected]
+W: https://www.displaylink.com
+S: Maintained
+F: drivers/usb/mausb_host/*
+
MCAN MMIO DEVICE DRIVER
M: Dan Murphy <[email protected]>
M: Sriram Dash <[email protected]>
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

2020-04-25 09:25:20

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 8/8] usb: mausb_host: Process MA-USB data packets

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

Signed-off-by: Vladimir Stankovic <[email protected]>
---
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

2020-04-25 09:25:46

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v5 2/8] usb: mausb_host: Add link layer implementation

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 <[email protected]>
---
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

2020-04-26 00:34:03

by Alan Stern

[permalink] [raw]
Subject: Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

On Sat, 25 Apr 2020 [email protected] wrote:

> Protocol adaptation layer (PAL) implementation has been added to
> introduce MA-USB structures and logic.
>
> Signed-off-by: Vladimir Stankovic <[email protected]>

...

> + /*
> + * 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

2020-04-26 12:35:53

by Vladimir Stankovic

[permalink] [raw]
Subject: Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

On 26.4.20. 02:32, Alan Stern wrote:
> On Sat, 25 Apr 2020 [email protected] wrote:
>
>> Protocol adaptation layer (PAL) implementation has been added to
>> introduce MA-USB structures and logic.
>>
>> Signed-off-by: Vladimir Stankovic <[email protected]>
>
> ...
>
>> + /*
>> + * 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.

2020-04-26 14:36:31

by Alan Stern

[permalink] [raw]
Subject: Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

On Sun, 26 Apr 2020, Vladimir Stankovic wrote:

> On 26.4.20. 02:32, Alan Stern wrote:
> > On Sat, 25 Apr 2020 [email protected] wrote:
> >
> >> Protocol adaptation layer (PAL) implementation has been added to
> >> introduce MA-USB structures and logic.
> >>
> >> Signed-off-by: Vladimir Stankovic <[email protected]>
> >
> > ...
> >
> >> + /*
> >> + * 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

2020-04-26 14:47:58

by Vladimir Stankovic

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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 [email protected] wrote:
>>>
>>>> Protocol adaptation layer (PAL) implementation has been added to
>>>> introduce MA-USB structures and logic.
>>>>
>>>> Signed-off-by: Vladimir Stankovic <[email protected]>
>>>
>>> ...
>>>
>>>> + /*
>>>> + * 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.

2020-04-26 21:01:19

by Alan Stern

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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 [email protected] wrote:
> >>>
> >>>> Protocol adaptation layer (PAL) implementation has been added to
> >>>> introduce MA-USB structures and logic.
> >>>>
> >>>> Signed-off-by: Vladimir Stankovic <[email protected]>
> >>>
> >>> ...
> >>>
> >>>> + /*
> >>>> + * 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

2020-04-28 11:07:45

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v5 1/8] usb: Add MA-USB Host kernel module

On Sat, Apr 25, 2020 at 11:19:47AM +0200, [email protected] 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 <[email protected]>
> ---
> 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 <[email protected]>
> +L: [email protected]
> +W: https://www.displaylink.com
> +S: Maintained
> +F: drivers/usb/mausb_host/*
> +
> MCAN MMIO DEVICE DRIVER
> M: Dan Murphy <[email protected]>
> M: Sriram Dash <[email protected]>
> 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

2020-04-28 11:09:33

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v5 0/8] Add MA USB Host driver

On Sat, Apr 25, 2020 at 11:19:46AM +0200, [email protected] 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

2020-04-28 11:11:03

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v5 7/8] usb: mausb_host: MA-USB PAL events processing

On Sat, Apr 25, 2020 at 11:19:53AM +0200, [email protected] 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

2020-04-30 14:40:36

by Vladimir Stankovic

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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 [email protected] wrote:
>>>>>
>>>>>> Protocol adaptation layer (PAL) implementation has been added to
>>>>>> introduce MA-USB structures and logic.
>>>>>>
>>>>>> Signed-off-by: Vladimir Stankovic <[email protected]>
>>>>>
>>>>> ...
>>>>>
>>>>>> + /*
>>>>>> + * 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.

2020-04-30 15:20:46

by Alan Stern

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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 [email protected] wrote:
> >>>>>
> >>>>>> Protocol adaptation layer (PAL) implementation has been added to
> >>>>>> introduce MA-USB structures and logic.
> >>>>>>
> >>>>>> Signed-off-by: Vladimir Stankovic <[email protected]>
> >>>>>
> >>>>> ...
> >>>>>
> >>>>>> + /*
> >>>>>> + * 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

2020-04-30 15:37:12

by Vladimir Stankovic

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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 [email protected] wrote:
>>>>>>>
>>>>>>>> Protocol adaptation layer (PAL) implementation has been added to
>>>>>>>> introduce MA-USB structures and logic.
>>>>>>>>
>>>>>>>> Signed-off-by: Vladimir Stankovic <[email protected]>
>>>>>>>
>>>>>>> ...
>>>>>>>
>>>>>>>> + /*
>>>>>>>> + * 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.

2020-04-30 15:44:26

by Alan Stern

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 5/8] usb: mausb_host: Introduce PAL processing

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 [email protected] wrote:
> >>>>>>>
> >>>>>>>> Protocol adaptation layer (PAL) implementation has been added to
> >>>>>>>> introduce MA-USB structures and logic.
> >>>>>>>>
> >>>>>>>> Signed-off-by: Vladimir Stankovic <[email protected]>
> >>>>>>>
> >>>>>>> ...
> >>>>>>>
> >>>>>>>> + /*
> >>>>>>>> + * 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

2020-04-30 16:55:19

by Vladimir Stankovic

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver

On 28.4.20. 13:04, Greg KH wrote:
> On Sat, Apr 25, 2020 at 11:19:46AM +0200, [email protected] 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.

2020-04-30 20:04:51

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver

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, [email protected] 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

2020-05-15 12:39:33

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v6 0/8] Add MA USB Host driver

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

2020-05-15 12:39:48

by Vladimir Stankovic

[permalink] [raw]
Subject: [PATCH v6 8/8] usb: mausb_host: Process MA-USB data packets

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

Signed-off-by: Vladimir Stankovic <[email protected]>
---
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

2020-05-15 13:07:10

by Vladimir Stankovic

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver

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, [email protected] 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.

2020-05-15 13:10:32

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH v6 0/8] Add MA USB Host driver

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

2020-05-29 12:51:15

by Pavel Machek

[permalink] [raw]
Subject: Re: [External] Re: [PATCH v5 0/8] Add MA USB Host driver

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