blob: 18766515f6c140b23591e1eba1b2cf6d6b8a4319 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 - 2021 DisplayLink (UK) Ltd.
*/
#include "hcd.h"
#include "hpal_events.h"
#include "utils.h"
static void mausb_hcd_remove(struct device *dev);
static int mausb_bus_match(struct device *dev, struct device_driver *drv);
struct mausb_hcd *mhcd;
static struct bus_type mausb_bus_type = {
.name = DRIVER_NAME,
.match = mausb_bus_match,
};
static struct device_driver mausb_driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.bus = &mausb_bus_type,
};
static struct device *mausb_device;
static void mausb_hcd_remove(struct device *dev)
{
struct usb_hcd *hcd;
struct usb_hcd *shared_hcd;
shared_hcd = mhcd->hcd_ss_ctx->hcd;
if (shared_hcd) {
usb_remove_hcd(shared_hcd);
usb_put_hcd(shared_hcd);
mhcd->hcd_ss_ctx = NULL;
}
hcd = mhcd->hcd_hs_ctx->hcd;
if (hcd) {
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
mhcd->hcd_hs_ctx = NULL;
}
kfree(mhcd);
mhcd = NULL;
}
static int mausb_bus_match(struct device *dev, struct device_driver *drv)
{
return !strncmp(dev->bus->name, drv->name, strlen(drv->name));
}
int mausb_host_driver_init(void)
{
int retval = bus_register(&mausb_bus_type);
if (retval)
return retval;
retval = driver_register(&mausb_driver);
if (retval)
goto cleanup_bus;
mausb_device = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!mausb_device) {
retval = -ENOMEM;
goto cleanup_driver;
}
dev_set_name(mausb_device, DEVICE_NAME);
mausb_device->bus = &mausb_bus_type;
mausb_device->release = (void (*)(struct device *))kfree;
retval = device_register(mausb_device);
if (retval) {
put_device(mausb_device);
goto cleanup_driver;
}
retval = mausb_hcd_create_and_add(mausb_device);
if (retval)
goto cleanup;
return retval;
cleanup:
device_unregister(mausb_device);
cleanup_driver:
driver_unregister(&mausb_driver);
cleanup_bus:
bus_unregister(&mausb_bus_type);
return retval;
}
void mausb_host_driver_deinit(void)
{
mausb_hcd_remove(mausb_device);
device_unregister(mausb_device);
driver_unregister(&mausb_driver);
bus_unregister(&mausb_bus_type);
}
void mausb_port_has_changed(const enum mausb_device_type device_type,
const enum mausb_device_speed device_speed,
void *ma_dev)
{
struct usb_hcd *hcd;
unsigned long flags;
struct mausb_device *dev = ma_dev;
u8 port_number = dev->port_number;
spin_lock_irqsave(&mhcd->lock, flags);
if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
USB_PORT_STAT_CONNECTION | (1 <<
USB_PORT_FEAT_C_CONNECTION);
if (device_speed == LOW_SPEED) {
mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
MAUSB_PORT_20_STATUS_LOW_SPEED;
mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
LOW_SPEED;
} else if (device_speed == HIGH_SPEED) {
mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
MAUSB_PORT_20_STATUS_HIGH_SPEED;
mhcd->hcd_hs_ctx->ma_devs[port_number].dev_speed =
HIGH_SPEED;
}
hcd = mhcd->hcd_hs_ctx->hcd;
mhcd->hcd_hs_ctx->ma_devs[port_number].ma_dev = ma_dev;
} else {
mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
USB_PORT_STAT_CONNECTION | (1 <<
USB_PORT_FEAT_C_CONNECTION);
mhcd->hcd_ss_ctx->ma_devs[port_number].dev_speed = SUPER_SPEED;
hcd = mhcd->hcd_ss_ctx->hcd;
mhcd->hcd_ss_ctx->ma_devs[port_number].ma_dev = ma_dev;
}
spin_unlock_irqrestore(&mhcd->lock, flags);
usb_hcd_poll_rh_status(hcd);
}
void mausb_hcd_disconnect(const u8 port_number,
const enum mausb_device_type device_type,
const enum mausb_device_speed device_speed)
{
struct usb_hcd *hcd;
unsigned long flags;
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);
if (device_type == USB20HUB || device_speed < SUPER_SPEED) {
mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
~(USB_PORT_STAT_CONNECTION);
mhcd->hcd_hs_ctx->ma_devs[port_number].port_status &=
~(USB_PORT_STAT_ENABLE);
mhcd->hcd_hs_ctx->ma_devs[port_number].port_status |=
(1 << USB_PORT_FEAT_C_CONNECTION);
hcd = mhcd->hcd_hs_ctx->hcd;
} else {
mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
~(USB_PORT_STAT_CONNECTION);
mhcd->hcd_ss_ctx->ma_devs[port_number].port_status &=
~(USB_PORT_STAT_ENABLE);
mhcd->hcd_ss_ctx->ma_devs[port_number].port_status |=
(1 << USB_PORT_FEAT_C_CONNECTION);
hcd = mhcd->hcd_ss_ctx->hcd;
}
spin_unlock_irqrestore(&mhcd->lock, flags);
if (!hcd)
return;
usb_hcd_poll_rh_status(hcd);
}
static const char driver_name[] = "MA-USB host controller";
static void mausb_get_hub_descriptor(struct usb_hcd *hcd, u16 type_req,
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 void mausb_reset_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_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
int status);
static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags);
static int mausb_hub_update_device(struct usb_hcd *hcd, struct usb_device *dev,
struct usb_tt *tt, gfp_t mem_flags);
static int mausb_reset_device(struct usb_hcd *hcd, struct usb_device *dev);
static int mausb_update_device(struct usb_hcd *hcd, struct usb_device *dev);
static void mausb_print_urb(struct urb *request)
{
dev_vdbg(&request->dev->dev, "URB: urb=%p, ep_handle=%#x, packet_num=%d, setup_dma=%pad, is_setup_packet=%d, is_ep=%d, is_sg=%d, num_sgs=%d, num_mapped_sgs=%d, status=%d, is_transfer_buffer=%d, transfer_buffer_length=%d, is_transfer_dma=%pad, transfer_flags=%d, is_hcpriv=%d",
request, ((struct mausb_endpoint_ctx *)
request->ep->hcpriv)->ep_handle,
request->number_of_packets, &request->setup_dma,
request->setup_packet ? 1 : 0, request->ep ? 1 : 0,
request->sg ? 1 : 0, request->num_sgs,
request->num_mapped_sgs, request->status,
request->transfer_buffer ? 1 : 0,
request->transfer_buffer_length,
&request->transfer_dma, request->transfer_flags,
(request->ep && request->ep->hcpriv) ? 1 : 0);
}
static const struct hc_driver mausb_hc_driver = {
.description = driver_name,
.product_desc = driver_name,
.flags = HCD_USB3 | HCD_SHARED,
.hcd_priv_size = sizeof(struct hub_ctx),
.reset = mausb_hcd_reset,
.start = mausb_hcd_start,
.stop = mausb_hcd_stop,
.urb_enqueue = mausb_hcd_urb_enqueue,
.urb_dequeue = mausb_hcd_urb_dequeue,
.get_frame_number = mausb_hcd_get_frame_number,
.hub_status_data = mausb_hcd_hub_status,
.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 int usb_to_mausb_device_speed(u8 speed)
{
switch (speed) {
case USB_SPEED_LOW:
return MA_USB_SPEED_LOW_SPEED;
case USB_SPEED_FULL:
return MA_USB_SPEED_FULL_SPEED;
case USB_SPEED_WIRELESS:
case USB_SPEED_HIGH:
return MA_USB_SPEED_HIGH_SPEED;
case USB_SPEED_SUPER:
return MA_USB_SPEED_SUPER_SPEED;
case USB_SPEED_SUPER_PLUS:
return MA_USB_SPEED_SUPER_SPEED_PLUS;
default:
return -EINVAL;
}
}
static struct mausb_usb_device_ctx *mausb_find_usb_device(struct mausb_dev
*mdevs, void *dev_addr)
{
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_insert_usb_device(struct mausb_dev *mdevs,
struct mausb_usb_device_ctx *usb_device)
{
struct rb_node **new_node = &mdevs->usb_devices.rb_node;
struct rb_node *parent = NULL;
struct mausb_usb_device_ctx *current_usb_device = NULL;
while (*new_node) {
parent = *new_node;
current_usb_device = rb_entry(*new_node,
struct mausb_usb_device_ctx,
rb_node);
if (usb_device->dev_addr < current_usb_device->dev_addr)
new_node = &((*new_node)->rb_left);
else if (usb_device->dev_addr > current_usb_device->dev_addr)
new_node = &((*new_node)->rb_right);
else
return -EEXIST;
}
rb_link_node(&usb_device->rb_node, parent, new_node);
rb_insert_color(&usb_device->rb_node, &mdevs->usb_devices);
return 0;
}
static int mausb_hcd_get_frame_number(struct usb_hcd *hcd)
{
return 0;
}
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)
{
dev_vdbg(mausb_host_dev.this_device, "HCD stopped");
}
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;
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;
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;
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;
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(&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(&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(&mhcd->lock, flags);
return retval;
}
static int mausb_validate_urb(struct urb *urb)
{
if (!urb) {
dev_err(mausb_host_dev.this_device, "urb is NULL");
return -EINVAL;
}
if (!urb->ep->hcpriv) {
dev_err(mausb_host_dev.this_device, "urb->ep->hcpriv is NULL");
return -EINVAL;
}
if (!urb->ep->enabled) {
dev_err(mausb_host_dev.this_device, "Endpoint not enabled");
return -EINVAL;
}
return 0;
}
static int mausb_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
struct mausb_endpoint_ctx *endpoint_ctx;
struct mausb_device *ma_dev;
struct mausb_urb_ctx *urb_ctx;
int status = 0;
if (mausb_validate_urb(urb) < 0) {
dev_err(&urb->dev->dev, "Hpal urb enqueue failed");
return -EPROTO;
}
endpoint_ctx = urb->ep->hcpriv;
ma_dev = endpoint_ctx->ma_dev;
if (atomic_read(&ma_dev->unresponsive_client)) {
dev_err(&urb->dev->dev, "Client is not responsive anymore - finish urb immediately");
return -EHOSTDOWN;
}
urb->hcpriv = hcd;
dev_vdbg(&urb->dev->dev, "ep_handle=%#x, dev_handle=%#x, urb_reject=%d",
endpoint_ctx->ep_handle, endpoint_ctx->dev_handle,
atomic_read(&urb->reject));
status = mausb_insert_urb_in_tree(urb, true);
if (status) {
dev_err(&urb->dev->dev, "Hpal urb enqueue failed");
return status;
}
atomic_inc(&urb->use_count);
mausb_print_urb(urb);
status = mausb_data_req_enqueue_event(ma_dev, endpoint_ctx->ep_handle,
urb);
if (status < 0) {
urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
atomic_dec(&urb->use_count);
if (urb_ctx) {
mausb_uninit_data_iterator(&urb_ctx->iterator);
kfree(urb_ctx);
}
}
return status;
}
static int mausb_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
int status)
{
struct mausb_endpoint_ctx *endpoint_ctx;
struct mausb_device *ma_dev;
struct mausb_urb_ctx *urb_ctx;
dev_info(&urb->dev->dev, "Urb=%p", urb);
urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status);
if (!urb_ctx) {
dev_warn(mausb_host_dev.this_device, "Urb=%p is not in tree",
urb);
return 0;
}
endpoint_ctx = urb->ep->hcpriv;
ma_dev = endpoint_ctx->ma_dev;
queue_work(ma_dev->workq, &urb_ctx->work);
return 0;
}
void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status)
{
struct mausb_urb_ctx *urb_ctx =
mausb_unlink_and_delete_urb_from_tree(urb, status);
if (urb_ctx) {
mausb_uninit_data_iterator(&urb_ctx->iterator);
kfree(urb_ctx);
urb->status = status;
urb->actual_length = actual_length;
atomic_dec(&urb->use_count);
usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
}
}
int mausb_hcd_create_and_add(struct device *dev)
{
int retval;
struct usb_hcd *hcd_ss;
struct usb_hcd *hcd_hs;
hcd_hs = usb_create_hcd(&mausb_hc_driver, dev, dev_name(dev));
if (!hcd_hs)
return -ENOMEM;
mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC);
if (!mhcd) {
retval = -ENOMEM;
goto cleanup_hcd_hs;
}
spin_lock_init(&mhcd->lock);
hcd_hs->has_tt = 1;
mhcd->hcd_hs_ctx = (struct hub_ctx *)hcd_hs->hcd_priv;
mhcd->hcd_hs_ctx->hcd = hcd_hs;
memset(mhcd->hcd_hs_ctx->ma_devs, 0,
sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
retval = usb_add_hcd(hcd_hs, 0, 0);
if (retval) {
dev_err(dev, "usb_add_hcd failed");
goto cleanup_mhcd;
}
hcd_ss = usb_create_shared_hcd(&mausb_hc_driver, dev, dev_name(dev),
hcd_hs);
if (!hcd_ss) {
retval = -ENOMEM;
goto remove_hcd_hs;
}
mhcd->hcd_ss_ctx = (struct hub_ctx *)hcd_ss->hcd_priv;
mhcd->hcd_ss_ctx->hcd = hcd_ss;
memset(mhcd->hcd_ss_ctx->ma_devs, 0,
sizeof(struct mausb_dev) * NUMBER_OF_PORTS);
retval = usb_add_hcd(hcd_ss, 0, 0);
if (retval) {
dev_err(dev, "usb_add_hcd failed");
goto cleanup;
}
return retval;
cleanup:
usb_put_hcd(hcd_ss);
remove_hcd_hs:
usb_remove_hcd(hcd_hs);
cleanup_mhcd:
kfree(mhcd);
mhcd = NULL;
cleanup_hcd_hs:
usb_put_hcd(hcd_hs);
return retval;
}
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)
{
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_device *ma_dev;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
port_number);
return;
}
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_ctx is not found");
return;
}
dev_handle = usb_device_ctx->dev_handle;
if (atomic_read(&ma_dev->unresponsive_client)) {
dev_err(mausb_host_dev.this_device, "Client is not responsive anymore - free usbdevice immediately");
dev->ep0.hcpriv = NULL;
kfree(ep_ctx);
goto free_dev;
}
if (ep_ctx) {
mausb_epinactivate_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle);
mausb_cleartransfers_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle);
mausb_epdelete_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle);
dev->ep0.hcpriv = NULL;
kfree(ep_ctx);
} else {
dev_warn(mausb_host_dev.this_device, "ep_ctx is NULL: dev_handle=%#x",
dev_handle);
}
if (dev_handle != DEV_HANDLE_NOT_ASSIGNED)
mausb_usbdevdisconnect_event_to_user(ma_dev, dev_handle);
free_dev:
if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) {
dev_info(mausb_host_dev.this_device, "All usb devices destroyed - proceed with disconnecting");
mausb_devdisconnect_event_to_user(ma_dev);
queue_work(ma_dev->workq, &ma_dev->socket_disconnect_work);
}
rb_erase(&usb_device_ctx->rb_node,
&hub->ma_devs[port_number].usb_devices);
dev_info(mausb_host_dev.this_device, "USB device deleted device=%p",
usb_device_ctx->dev_addr);
kfree(usb_device_ctx);
if (kref_put(&ma_dev->refcount, mausb_release_ma_dev_async))
mausb_clear_hcd_madev(port_number);
}
static int mausb_device_assign_address(struct mausb_device *dev,
struct mausb_usb_device_ctx *usb_dev_ctx)
{
int status =
mausb_setusbdevaddress_event_to_user(dev,
usb_dev_ctx->dev_handle,
RESPONSE_TIMEOUT_MS);
usb_dev_ctx->addressed = (status == 0);
return status;
}
static struct mausb_usb_device_ctx *
mausb_alloc_device_ctx(struct hub_ctx *hub, struct usb_device *dev,
struct mausb_device *ma_dev, u8 port_number,
int *status)
{
struct mausb_usb_device_ctx *usb_device_ctx = NULL;
usb_device_ctx = kzalloc(sizeof(*usb_device_ctx), GFP_ATOMIC);
if (!usb_device_ctx) {
*status = -ENOMEM;
return NULL;
}
usb_device_ctx->dev_addr = dev;
usb_device_ctx->dev_handle = DEV_HANDLE_NOT_ASSIGNED;
usb_device_ctx->addressed = false;
if (mausb_insert_usb_device(&hub->ma_devs[port_number],
usb_device_ctx)) {
dev_warn(&dev->dev, "device_ctx already exists");
kfree(usb_device_ctx);
*status = -EEXIST;
return NULL;
}
kref_get(&ma_dev->refcount);
dev_info(&dev->dev, "New USB device added device=%p",
usb_device_ctx->dev_addr);
atomic_inc(&ma_dev->num_of_usb_devices);
return 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_device *ma_dev;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_warn(&dev->dev, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
if (!usb_device_ctx) {
usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
port_number, &status);
if (!usb_device_ctx)
return status;
}
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;
}
if (!usb_device_ctx->addressed) {
status = mausb_device_assign_address(ma_dev, usb_device_ctx);
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;
}
/*
* If endpoint handle of usb 2.0 device is already modified,
* do not modify it again.
*/
if (dev->speed < USB_SPEED_SUPER && endpoint_ctx->ep_handle != 0)
return 0;
status = mausb_modifyep0_event_to_user(ma_dev,
usb_device_ctx->dev_handle,
&endpoint_ctx->ep_handle,
dev->ep0.desc.wMaxPacketSize);
return status;
}
static int mausb_add_endpoint(struct usb_hcd *hcd, struct usb_device *dev,
struct usb_host_endpoint *endpoint)
{
int status;
u8 port_number;
struct ma_usb_ephandlereq_desc_ss descriptor_ss;
struct ma_usb_ephandlereq_desc_std descriptor;
struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
struct mausb_device *ma_dev;
struct mausb_usb_device_ctx *usb_dev_ctx;
struct mausb_endpoint_ctx *endpoint_ctx;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(&dev->dev, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
if (!usb_dev_ctx) {
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_ctx->ma_dev = ma_dev;
endpoint->hcpriv = endpoint_ctx;
if (dev->speed >= USB_SPEED_SUPER) {
mausb_init_superspeed_ep_descriptor(&descriptor_ss,
&endpoint->desc,
&endpoint->ss_ep_comp);
status = mausb_ephandle_event_to_user(ma_dev,
usb_dev_ctx->dev_handle,
sizeof(descriptor_ss),
&descriptor_ss,
&endpoint_ctx->ep_handle);
} else {
mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
status = mausb_ephandle_event_to_user(ma_dev,
usb_dev_ctx->dev_handle,
sizeof(descriptor),
&descriptor,
&endpoint_ctx->ep_handle);
}
if (status < 0) {
endpoint->hcpriv = NULL;
kfree(endpoint_ctx);
return status;
}
dev_info(&dev->dev, "Endpoint added ep_handle=%#x, dev_handle=%#x",
endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
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;
int retries = 0;
struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
struct mausb_device *ma_dev;
struct mausb_usb_device_ctx *usb_dev_ctx;
struct mausb_endpoint_ctx *endpoint_ctx = endpoint->hcpriv;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(&dev->dev, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_dev_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
if (!endpoint_ctx) {
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;
}
dev_info(&dev->dev, "Start dropping ep_handle=%#x, dev_handle=%#x",
endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
if (atomic_read(&ma_dev->unresponsive_client)) {
dev_err(&dev->dev, "Client is not responsive anymore - drop endpoint immediately");
endpoint->hcpriv = NULL;
kfree(endpoint_ctx);
return -ESHUTDOWN;
}
mausb_epinactivate_event_to_user(ma_dev, usb_dev_ctx->dev_handle,
endpoint_ctx->ep_handle);
if (!usb_endpoint_xfer_isoc(&endpoint->desc))
mausb_cleartransfers_event_to_user(ma_dev,
usb_dev_ctx->dev_handle,
endpoint_ctx->ep_handle);
while (true) {
status = mausb_epdelete_event_to_user(ma_dev,
usb_dev_ctx->dev_handle,
endpoint_ctx->ep_handle);
if (status == -EBUSY) {
if (++retries < MAUSB_BUSY_RETRIES_COUNT)
usleep_range(9000, 10000);
else
return -EBUSY;
} else {
break;
}
}
dev_info(&dev->dev, "Endpoint dropped ep_handle=%#x, dev_handle=%#x",
endpoint_ctx->ep_handle, endpoint_ctx->dev_handle);
endpoint->hcpriv = NULL;
kfree(endpoint_ctx);
return status;
}
static int mausb_device_assign_dev_handle(struct usb_hcd *hcd,
struct usb_device *dev,
struct hub_ctx *hub,
struct mausb_device *ma_dev,
struct mausb_usb_device_ctx
*usb_device_ctx)
{
u8 port_number;
int status;
int dev_speed;
u16 hub_dev_handle = 0;
u16 parent_hs_hub_dev_handle = 0;
u16 parent_hs_hub_port = 0;
struct usb_device *first_hub_device = dev;
struct mausb_usb_device_ctx *hub_device_ctx;
struct mausb_endpoint_ctx *endpoint_ctx;
struct ma_usb_ephandlereq_desc_std descriptor;
status = get_root_hub_port_number(dev, &port_number);
if (status < 0 || port_number >= NUMBER_OF_PORTS) {
dev_dbg(mausb_host_dev.this_device, "port_number out of range, port_number=%x",
port_number);
return -EINVAL;
}
while (first_hub_device->parent->parent)
first_hub_device = first_hub_device->parent;
hub_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
first_hub_device);
if (hub_device_ctx)
hub_dev_handle = hub_device_ctx->dev_handle;
if ((dev->speed == USB_SPEED_LOW || dev->speed == USB_SPEED_FULL) &&
first_hub_device->speed == USB_SPEED_HIGH) {
parent_hs_hub_dev_handle =
mausb_find_usb_device(&hub->ma_devs[port_number],
dev->parent)->dev_handle;
parent_hs_hub_port = dev->parent->portnum;
}
dev_speed = usb_to_mausb_device_speed(dev->speed);
dev_info(mausb_host_dev.this_device, "start... mausb_devspeed=%d, route=%#x, port_number=%d",
dev_speed, dev->route, port_number);
if (dev_speed == -EINVAL) {
dev_err(mausb_host_dev.this_device, "bad dev_speed");
return -EINVAL;
}
status = mausb_usbdevhandle_event_to_user(ma_dev, (u8)dev_speed,
dev->route, hub_dev_handle,
parent_hs_hub_dev_handle,
parent_hs_hub_port, 0,
ma_dev->lse,
&usb_device_ctx->dev_handle);
if (status < 0)
return status;
dev_vdbg(mausb_host_dev.this_device, "dev_handle=%#x",
usb_device_ctx->dev_handle);
endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC);
if (!endpoint_ctx)
return -ENOMEM;
endpoint_ctx->dev_handle = usb_device_ctx->dev_handle;
endpoint_ctx->ma_dev = ma_dev;
endpoint_ctx->usb_device_ctx = usb_device_ctx;
dev->ep0.hcpriv = endpoint_ctx;
mausb_init_standard_ep_descriptor(&descriptor, &dev->ep0.desc);
status = mausb_ephandle_event_to_user(ma_dev,
usb_device_ctx->dev_handle,
sizeof(descriptor),
&descriptor,
&endpoint_ctx->ep_handle);
if (status < 0) {
dev->ep0.hcpriv = NULL;
kfree(endpoint_ctx);
return status;
}
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_device *ma_dev;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
if (!usb_device_ctx) {
usb_device_ctx = mausb_alloc_device_ctx(hub, dev, ma_dev,
port_number, &status);
if (!usb_device_ctx)
return status;
}
if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev,
usb_device_ctx);
if (!usb_device_ctx->addressed)
return mausb_device_assign_address(ma_dev, usb_device_ctx);
dev_vdbg(mausb_host_dev.this_device, "Device assigned and addressed, usb_device_ctx=%p, dev_handle=%#x",
usb_device_ctx, usb_device_ctx->dev_handle);
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_device *ma_dev = NULL;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
if (!usb_device_ctx) {
dev_err(mausb_host_dev.this_device, "Device not found");
return -ENODEV;
}
status = mausb_updatedev_event_to_user(ma_dev,
usb_device_ctx->dev_handle,
0, 0, 0, 0, 0, 0,
&dev->descriptor);
dev_vdbg(mausb_host_dev.this_device, "Finished dev_handle=%#x, status=%d",
usb_device_ctx->dev_handle, status);
return status;
}
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 number_of_ports = (u8)dev->maxchild;
u8 mtt = 0;
u8 ttt = 0;
u8 integrated_hub_latency = 0;
struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv;
struct mausb_device *ma_dev;
struct mausb_usb_device_ctx *usb_device_ctx;
if (dev->speed == USB_SPEED_HIGH) {
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number],
dev);
if (!usb_device_ctx) {
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;
status = mausb_updatedev_event_to_user(ma_dev,
usb_device_ctx->dev_handle,
max_exit_latency, 1,
number_of_ports, mtt, ttt,
integrated_hub_latency,
&dev->descriptor);
dev_vdbg(mausb_host_dev.this_device, "Finished dev_handle=%#x, status=%d",
usb_device_ctx->dev_handle, status);
return status;
}
static int mausb_check_bandwidth(struct usb_hcd *hcd, struct usb_device *dev)
{
return 0;
}
static void mausb_reset_bandwidth(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)
{
int status;
int is_control;
int epnum;
int is_out;
u16 dev_handle;
u8 tsp;
u8 port_number;
struct hub_ctx *hub;
struct mausb_device *ma_dev;
struct mausb_usb_device_ctx *usb_device_ctx;
struct usb_device *dev;
struct mausb_endpoint_ctx *ep_ctx;
struct ma_usb_ephandlereq_desc_ss descriptor_ss;
struct ma_usb_ephandlereq_desc_std descriptor;
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;
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(&dev->dev, "MAUSB device not found on port_number=%d",
port_number);
return;
}
is_control = usb_endpoint_xfer_control(&endpoint->desc);
epnum = usb_endpoint_num(&endpoint->desc);
is_out = usb_endpoint_dir_out(&endpoint->desc);
tsp = (u8)(is_out ? dev->toggle[1] : dev->toggle[0]);
status = mausb_epreset_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle, tsp);
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);
}
mausb_epactivate_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle);
return;
}
if (tsp)
return;
status = mausb_epinactivate_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle);
if (status < 0)
return;
status = mausb_epdelete_event_to_user(ma_dev, dev_handle,
ep_ctx->ep_handle);
if (status < 0)
return;
if (dev->speed >= USB_SPEED_SUPER) {
mausb_init_superspeed_ep_descriptor(&descriptor_ss,
&endpoint->desc,
&endpoint->ss_ep_comp);
mausb_ephandle_event_to_user(ma_dev, dev_handle,
sizeof(descriptor_ss),
&descriptor_ss,
&ep_ctx->ep_handle);
} else {
mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc);
mausb_ephandle_event_to_user(ma_dev, dev_handle,
sizeof(descriptor), &descriptor,
&ep_ctx->ep_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_device *ma_dev;
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;
}
ma_dev = hub->ma_devs[port_number].ma_dev;
if (!ma_dev) {
dev_err(mausb_host_dev.this_device, "MAUSB device not found on port_number=%d",
port_number);
return -ENODEV;
}
usb_device_ctx = mausb_find_usb_device(&hub->ma_devs[port_number], dev);
if (!usb_device_ctx ||
usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED)
return 0;
dev_handle = usb_device_ctx->dev_handle;
status = mausb_usbdevreset_event_to_user(ma_dev, dev_handle);
if (status == 0)
usb_device_ctx->addressed = false;
return status;
}
void mausb_clear_hcd_madev(u8 port_number)
{
unsigned long flags;
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);
}