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