[PATCH v3 4/8] usb: mausb_host: Implement initial hub handlers

From: Vladimir Stankovic
Date: Thu Mar 12 2020 - 10:43:26 EST


Implemented handlers for subset of HCD events.

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

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