lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20200327152614.26833-5-vladimir.stankovic@displaylink.com>
Date:   Fri, 27 Mar 2020 16:26:10 +0100
From:   vladimir.stankovic@...playlink.com
To:     gregkh@...uxfoundation.org
Cc:     linux-kernel@...r.kernel.org, linux-usb@...r.kernel.org,
        mausb-host-devel@...playlink.com
Subject: [PATCH v4 4/8] usb: mausb_host: Implement initial hub handlers

Implemented handlers for subset of HCD events.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@...playlink.com>
---
 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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ