blob: 42602ae0740dfb14840dbe414ff50924fd610b20 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 - 2021 DisplayLink (UK) Ltd.
*/
#include "hpal_events.h"
#include "hcd.h"
#include "utils.h"
void mausb_dev_reset_req_event(struct mausb_event *event)
{
event->type = MAUSB_EVENT_TYPE_DEV_RESET;
}
static int mausb_mgmt_msg_received_event(struct mausb_event *event,
struct ma_usb_hdr_common *hdr,
enum mausb_channel channel)
{
int status = 0;
dev_vdbg(mausb_host_dev.this_device, "channel=%d, type=%d", channel,
hdr->type);
if (hdr->length <= MAUSB_MAX_MGMT_SIZE) {
event->type = MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG;
memcpy(event->mgmt.mgmt_hdr.hdr, hdr, hdr->length);
} else {
dev_err(mausb_host_dev.this_device, "MGMT message to long, failed to copy");
status = -EINVAL;
}
kfree(hdr);
return status;
}
static int mausb_data_msg_received_event(struct mausb_event *event,
struct ma_usb_hdr_common *hdr,
enum mausb_channel channel)
{
event->type = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
event->data.device_id = (u16)((hdr->ssid << 8) | hdr->dev_addr);
event->data.ep_handle = hdr->handle.epv;
event->data.recv_buf = (uintptr_t)hdr;
memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
if (mausb_ctrl_transfer(hdr) &&
hdr->length <= 2 * MAUSB_TRANSFER_HDR_SIZE) {
memcpy(event->data.hdr_ack,
shift_ptr(hdr, MAUSB_TRANSFER_HDR_SIZE),
(size_t)(hdr->length - MAUSB_TRANSFER_HDR_SIZE));
}
return 0;
}
static int mausb_isoch_msg_received_event(struct mausb_event *event,
struct ma_usb_hdr_common *hdr,
enum mausb_channel channel)
{
event->type = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG;
event->data.transfer_type = mausb_transfer_type_from_hdr(hdr);
event->data.device_id = (u16)((hdr->ssid << 8) | hdr->dev_addr);
event->data.ep_handle = hdr->handle.epv;
event->data.recv_buf = (uintptr_t)hdr;
memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE);
return 0;
}
int mausb_msg_received_event(struct mausb_event *event,
struct ma_usb_hdr_common *hdr,
enum mausb_channel channel)
{
dev_vdbg(mausb_host_dev.this_device, "channel=%d, type=%d", channel,
hdr->type);
if (mausb_is_management_hdr_type(hdr->type))
return mausb_mgmt_msg_received_event(event, hdr, channel);
else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(TRANSFER))
return mausb_data_msg_received_event(event, hdr, channel);
else if (hdr->type == MA_USB_HDR_TYPE_DATA_RESP(ISOCHTRANSFER))
return mausb_isoch_msg_received_event(event, hdr, channel);
dev_warn(mausb_host_dev.this_device, "Unknown event type event=%d",
hdr->type);
kfree(hdr);
return -EBADR;
}
static void mausb_prepare_completion(struct mausb_completion *mausb_completion,
struct completion *completion,
struct mausb_event *event, u64 event_id)
{
init_completion(completion);
mausb_completion->completion_event = completion;
mausb_completion->event_id = event_id;
mausb_completion->mausb_event = event;
}
static int mausb_wait_for_completion(struct mausb_event *event, u64 event_id,
struct mausb_device *dev)
{
struct completion completion;
struct mausb_completion mausb_completion;
long status;
unsigned long timeout;
mausb_prepare_completion(&mausb_completion, &completion, event,
event_id);
mausb_insert_event(dev, &mausb_completion);
status = mausb_enqueue_event_to_user(dev, event);
if (status < 0) {
mausb_remove_event(dev, &mausb_completion);
dev_err(mausb_host_dev.this_device, "Ring buffer full, event_id=%lld",
event_id);
return (int)status;
}
timeout = msecs_to_jiffies(MANAGEMENT_EVENT_TIMEOUT);
status = wait_for_completion_interruptible_timeout(&completion,
timeout);
mausb_remove_event(dev, &mausb_completion);
if (status == 0) {
queue_work(dev->workq, &dev->socket_disconnect_work);
queue_work(dev->workq, &dev->hcd_disconnect_work);
return -ETIMEDOUT;
}
return 0;
}
int mausb_usbdevhandle_event_to_user(struct mausb_device *dev,
u8 device_speed,
u32 route_string,
u16 hub_dev_handle,
u16 parent_hs_hub_dev_handle,
u16 parent_hs_hub_port, u16 mtt,
u8 lse, s32 *usb_dev_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_USB_DEV_HANDLE;
event.mgmt.dev_handle.device_speed = device_speed;
event.mgmt.dev_handle.route_string = route_string;
event.mgmt.dev_handle.hub_dev_handle = hub_dev_handle;
event.mgmt.dev_handle.parent_hs_hub_port = parent_hs_hub_port;
event.mgmt.dev_handle.mtt = mtt;
event.mgmt.dev_handle.lse = lse;
event.mgmt.dev_handle.event_id = event_id;
event.madev_addr = dev->madev_addr;
event.mgmt.dev_handle.parent_hs_hub_dev_handle =
parent_hs_hub_dev_handle;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "USBDevHandle failed, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.dev_handle.dev_handle, event_id, status);
return status;
}
if (event.status < 0)
return event.status;
*usb_dev_handle = event.mgmt.dev_handle.dev_handle;
return 0;
}
int mausb_usbdevhandle_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event, event->mgmt.dev_handle.event_id);
}
int mausb_ephandle_event_to_user(struct mausb_device *dev,
u16 device_handle,
u16 descriptor_size, void *descriptor,
u16 *ep_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_EP_HANDLE;
event.mgmt.ep_handle.device_handle = device_handle;
event.mgmt.ep_handle.descriptor_size = descriptor_size;
event.mgmt.ep_handle.event_id = event_id;
event.madev_addr = dev->madev_addr;
memcpy(event.mgmt.ep_handle.descriptor, descriptor, descriptor_size);
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "EPHandle failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.ep_handle.ep_handle, device_handle,
event_id, status);
return status;
}
if (event.status < 0)
return event.status;
*ep_handle = event.mgmt.ep_handle.ep_handle;
return 0;
}
int mausb_ephandle_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event, event->mgmt.ep_handle.event_id);
}
int mausb_epactivate_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 ep_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE;
event.mgmt.ep_activate.device_handle = device_handle;
event.mgmt.ep_activate.ep_handle = ep_handle;
event.mgmt.ep_activate.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "Epactivate failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.ep_activate.ep_handle, device_handle,
event_id, status);
return status;
}
return event.status;
}
int mausb_epactivate_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event,
event->mgmt.ep_activate.event_id);
}
int mausb_epinactivate_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 ep_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE;
event.mgmt.ep_inactivate.device_handle = device_handle;
event.mgmt.ep_inactivate.ep_handle = ep_handle;
event.mgmt.ep_inactivate.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "EPInactivate failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.ep_inactivate.ep_handle, device_handle,
event_id, status);
return status;
}
return event.status;
}
int mausb_epinactivate_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event,
event->mgmt.ep_inactivate.event_id);
}
int mausb_epreset_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 ep_handle,
u8 tsp_flag)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_EP_HANDLE_RESET;
event.mgmt.ep_reset.device_handle = device_handle;
event.mgmt.ep_reset.ep_handle = ep_handle;
event.mgmt.ep_reset.tsp = tsp_flag;
event.mgmt.ep_reset.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "EPReset failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.ep_reset.ep_handle, device_handle, event_id,
status);
return status;
}
return event.status;
}
int mausb_epreset_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event, event->mgmt.ep_reset.event_id);
}
int mausb_epdelete_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 ep_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_EP_HANDLE_DELETE;
event.mgmt.ep_delete.device_handle = device_handle;
event.mgmt.ep_delete.ep_handle = ep_handle;
event.mgmt.ep_delete.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "EPDelete failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.ep_delete.ep_handle, device_handle,
event_id, status);
return status;
}
return event.status;
}
int mausb_epdelete_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event, event->mgmt.ep_delete.event_id);
}
int mausb_modifyep0_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 *ep_handle,
__le16 max_packet_size)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_MODIFY_EP0;
event.mgmt.modify_ep0.device_handle = device_handle;
event.mgmt.modify_ep0.ep_handle = *ep_handle;
event.mgmt.modify_ep0.max_packet_size = max_packet_size;
event.mgmt.modify_ep0.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "ModifyEP0 failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.modify_ep0.ep_handle, device_handle,
event_id, status);
return status;
}
if (event.status < 0)
return event.status;
*ep_handle = event.mgmt.modify_ep0.ep_handle;
return 0;
}
int mausb_modifyep0_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event, event->mgmt.modify_ep0.event_id);
}
int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev,
u16 device_handle,
u16 response_timeout)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS;
event.mgmt.set_usb_dev_address.device_handle = device_handle;
event.mgmt.set_usb_dev_address.response_timeout = response_timeout;
event.mgmt.set_usb_dev_address.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "SetUSBDevAddress failed, dev_handle=%#x, event_id=%lld, status=%d",
device_handle, event_id, status);
return status;
}
return event.status;
}
int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event,
event->mgmt.set_usb_dev_address.event_id);
}
static void
mausb_init_device_descriptor(struct ma_usb_updatedevreq_desc *update_descriptor,
struct usb_device_descriptor *device_descriptor)
{
update_descriptor->usb20.bLength = device_descriptor->bLength;
update_descriptor->usb20.bDescriptorType =
device_descriptor->bDescriptorType;
update_descriptor->usb20.bcdUSB = device_descriptor->bcdUSB;
update_descriptor->usb20.bDeviceClass =
device_descriptor->bDeviceClass;
update_descriptor->usb20.bDeviceSubClass =
device_descriptor->bDeviceSubClass;
update_descriptor->usb20.bDeviceProtocol =
device_descriptor->bDeviceProtocol;
update_descriptor->usb20.bMaxPacketSize0 =
device_descriptor->bMaxPacketSize0;
update_descriptor->usb20.idVendor = device_descriptor->idVendor;
update_descriptor->usb20.idProduct = device_descriptor->idProduct;
update_descriptor->usb20.bcdDevice = device_descriptor->bcdDevice;
update_descriptor->usb20.iManufacturer =
device_descriptor->iManufacturer;
update_descriptor->usb20.iProduct = device_descriptor->iProduct;
update_descriptor->usb20.iSerialNumber =
device_descriptor->iSerialNumber;
update_descriptor->usb20.bNumConfigurations =
device_descriptor->bNumConfigurations;
}
int mausb_updatedev_event_to_user(struct mausb_device *dev,
u16 device_handle,
u16 max_exit_latency, u8 hub,
u8 number_of_ports, u8 mtt,
u8 ttt, u8 integrated_hub_latency,
struct usb_device_descriptor *dev_descriptor)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_UPDATE_DEV;
event.mgmt.update_dev.device_handle = device_handle;
event.mgmt.update_dev.max_exit_latency = max_exit_latency;
event.mgmt.update_dev.hub = hub;
event.mgmt.update_dev.number_of_ports = number_of_ports;
event.mgmt.update_dev.mtt = mtt;
event.mgmt.update_dev.ttt = ttt;
event.mgmt.update_dev.integrated_hub_latency = integrated_hub_latency;
event.mgmt.update_dev.event_id = event_id;
event.madev_addr = dev->madev_addr;
mausb_init_device_descriptor(&event.mgmt.update_dev.update_descriptor,
dev_descriptor);
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "UpdateDev failed, dev_handle=%#x, event_id=%lld, status=%d",
device_handle, event_id, status);
return status;
}
return event.status;
}
int mausb_updatedev_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event, event->mgmt.update_dev.event_id);
}
int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev,
u16 dev_handle)
{
struct mausb_event event;
int status;
event.type = MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT;
event.mgmt.usb_dev_disconnect.device_handle = dev_handle;
event.madev_addr = dev->madev_addr;
status = mausb_enqueue_event_to_user(dev, &event);
if (status < 0)
dev_warn(mausb_host_dev.this_device, "USBDevDisconnect failed, dev_handle=%#x, status=%d",
dev_handle, status);
return status;
}
int mausb_ping_event_to_user(struct mausb_device *dev)
{
struct mausb_event event;
int status;
event.type = MAUSB_EVENT_TYPE_PING;
event.madev_addr = dev->madev_addr;
status = mausb_enqueue_event_to_user(dev, &event);
if (status < 0)
dev_warn(mausb_host_dev.this_device, "Ping failed, status=%d",
status);
return status;
}
int mausb_devdisconnect_event_to_user(struct mausb_device *dev)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT;
event.mgmt.dev_disconnect.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "DevDisconnect failed, event_id=%lld, status=%d",
event_id, status);
return status;
}
return event.status;
}
int mausb_devdisconnect_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event,
event->mgmt.dev_disconnect.event_id);
}
int mausb_usbdevreset_event_to_user(struct mausb_device *dev,
u16 device_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_USB_DEV_RESET;
event.mgmt.usb_dev_reset.device_handle = device_handle;
event.mgmt.usb_dev_reset.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "UsbDevReset failed, dev_handle=%#x, event_id=%lld, status=%d",
device_handle, event_id, status);
return status;
}
return event.status;
}
int mausb_usbdevreset_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event,
event->mgmt.usb_dev_reset.event_id);
}
int mausb_canceltransfer_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 ep_handle,
uintptr_t urb)
{
struct mausb_event event;
int status;
event.type = MAUSB_EVENT_TYPE_CANCEL_TRANSFER;
event.mgmt.cancel_transfer.device_handle = device_handle;
event.mgmt.cancel_transfer.ep_handle = ep_handle;
event.mgmt.cancel_transfer.urb = urb;
event.madev_addr = dev->madev_addr;
status = mausb_enqueue_event_to_user(dev, &event);
if (status < 0)
dev_warn(mausb_host_dev.this_device, "CancelTransfer failed, ep_handle=%#x, dev_handle=%#x, status=%d",
event.mgmt.modify_ep0.ep_handle, device_handle,
status);
return status;
}
int mausb_canceltransfer_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return 0;
}
int mausb_cleartransfers_event_to_user(struct mausb_device *dev,
u16 device_handle, u16 ep_handle)
{
struct mausb_event event;
int status;
u64 event_id = mausb_event_id(dev);
event.type = MAUSB_EVENT_TYPE_CLEAR_TRANSFERS;
event.mgmt.clear_transfers.device_handle = device_handle;
event.mgmt.clear_transfers.ep_handle = ep_handle;
event.mgmt.clear_transfers.event_id = event_id;
event.madev_addr = dev->madev_addr;
status = mausb_wait_for_completion(&event, event_id, dev);
if (status < 0) {
dev_warn(mausb_host_dev.this_device, "ClearTransfers failed, ep_handle=%#x, dev_handle=%#x, event_id=%lld, status=%d",
event.mgmt.clear_transfers.ep_handle, device_handle,
event_id, status);
return status;
}
return event.status;
}
int mausb_cleartransfers_event_from_user(struct mausb_device *dev,
struct mausb_event *event)
{
return mausb_signal_event(dev, event,
event->mgmt.clear_transfers.event_id);
}
void mausb_cleanup_send_data_msg_event(struct mausb_event *event)
{
mausb_complete_urb(event);
}
void mausb_cleanup_received_data_msg_event(struct mausb_event *event)
{
mausb_release_event_resources(event);
}
void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event)
{
struct urb *urb = (struct urb *)event->data.urb;
struct mausb_urb_ctx *urb_ctx;
int status = 0;
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;
}
/* Deallocate urb_ctx */
mausb_uninit_data_iterator(&urb_ctx->iterator);
kfree(urb_ctx);
urb->status = -EPROTO;
urb->actual_length = 0;
atomic_dec(&urb->use_count);
usb_hcd_giveback_urb(urb->hcpriv, urb, urb->status);
}