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