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