Implemented handlers for subset of HCD events.

Signed-off-by: Vladimir Stankovic <[email protected]>
---
 drivers/usb/host/mausb/hcd.c | 925 +++++++++++++++++++++++++++++++++++
 drivers/usb/host/mausb/hcd.h |  86 +++-
 2 files changed, 1008 insertions(+), 3 deletions(-)

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

Reply via email to