From 0324b3a49d0ef41480a0a42ab27f70edeb007980 Mon Sep 17 00:00:00 2001 From: Vladimir Stankovic Date: Tue, 11 Feb 2020 16:03:43 +0100 Subject: [PATCH] usb: Add MA USB Host driver MA-USB Host driver provides USB connectivity over an available network, allowing host device to access remote USB devices attached to one or more MA USB devices (accessible via network). This driver has been developed to enable the host to communicate with DisplayLink products supporting MA USB protocol (MA USB device, in terms of MA USB Specification). MA-USB protocol used by MA-USB Host driver has been implemented in accordance with MA USB Specification Release 1.0b. This driver depends on the functions provided by DisplayLink's user-space driver. Signed-off-by: Vladimir Stankovic --- MAINTAINERS | 6 + drivers/usb/Kconfig | 2 + drivers/usb/Makefile | 2 + drivers/usb/mausb_host/Kconfig | 15 + drivers/usb/mausb_host/Makefile | 30 + .../usb/mausb_host/include/common/ma_usb.h | 873 +++++++++ .../mausb_host/include/common/mausb_address.h | 38 + .../include/common/mausb_driver_status.h | 21 + .../mausb_host/include/common/mausb_event.h | 228 +++ drivers/usb/mausb_host/include/hcd/hub.h | 115 ++ drivers/usb/mausb_host/include/hcd/vhcd.h | 79 + .../usb/mausb_host/include/hpal/data_common.h | 66 + drivers/usb/mausb_host/include/hpal/data_in.h | 18 + .../usb/mausb_host/include/hpal/data_out.h | 22 + drivers/usb/mausb_host/include/hpal/hpal.h | 184 ++ .../usb/mausb_host/include/hpal/isoch_in.h | 22 + .../usb/mausb_host/include/hpal/isoch_out.h | 20 + .../mausb_host/include/hpal/mausb_events.h | 99 + .../include/hpal/network_callbacks.h | 20 + .../mausb_host/include/link/mausb_ip_link.h | 87 + .../include/utils/mausb_data_iterator.h | 52 + .../usb/mausb_host/include/utils/mausb_logs.h | 35 + .../usb/mausb_host/include/utils/mausb_mmap.h | 21 + .../include/utils/mausb_ring_buffer.h | 67 + .../mausb_host/include/utils/mausb_version.h | 17 + drivers/usb/mausb_host/src/hcd/hub.c | 1682 +++++++++++++++++ drivers/usb/mausb_host/src/hcd/module_init.c | 219 +++ drivers/usb/mausb_host/src/hcd/vhcd.c | 291 +++ drivers/usb/mausb_host/src/hpal/data_common.c | 141 ++ drivers/usb/mausb_host/src/hpal/data_in.c | 73 + drivers/usb/mausb_host/src/hpal/data_out.c | 202 ++ drivers/usb/mausb_host/src/hpal/hpal.c | 1306 +++++++++++++ drivers/usb/mausb_host/src/hpal/isoch_in.c | 241 +++ drivers/usb/mausb_host/src/hpal/isoch_out.c | 360 ++++ .../usb/mausb_host/src/hpal/mausb_events.c | 659 +++++++ .../mausb_host/src/hpal/network_callbacks.c | 160 ++ .../usb/mausb_host/src/link/mausb_ip_link.c | 354 ++++ .../src/utils/mausb_data_iterator.c | 295 +++ drivers/usb/mausb_host/src/utils/mausb_mmap.c | 381 ++++ .../mausb_host/src/utils/mausb_ring_buffer.c | 168 ++ 40 files changed, 8671 insertions(+) create mode 100644 drivers/usb/mausb_host/Kconfig create mode 100644 drivers/usb/mausb_host/Makefile create mode 100644 drivers/usb/mausb_host/include/common/ma_usb.h create mode 100644 drivers/usb/mausb_host/include/common/mausb_address.h create mode 100644 drivers/usb/mausb_host/include/common/mausb_driver_status.h create mode 100644 drivers/usb/mausb_host/include/common/mausb_event.h create mode 100644 drivers/usb/mausb_host/include/hcd/hub.h create mode 100644 drivers/usb/mausb_host/include/hcd/vhcd.h create mode 100644 drivers/usb/mausb_host/include/hpal/data_common.h create mode 100644 drivers/usb/mausb_host/include/hpal/data_in.h create mode 100644 drivers/usb/mausb_host/include/hpal/data_out.h create mode 100644 drivers/usb/mausb_host/include/hpal/hpal.h create mode 100644 drivers/usb/mausb_host/include/hpal/isoch_in.h create mode 100644 drivers/usb/mausb_host/include/hpal/isoch_out.h create mode 100644 drivers/usb/mausb_host/include/hpal/mausb_events.h create mode 100644 drivers/usb/mausb_host/include/hpal/network_callbacks.h create mode 100644 drivers/usb/mausb_host/include/link/mausb_ip_link.h create mode 100644 drivers/usb/mausb_host/include/utils/mausb_data_iterator.h create mode 100644 drivers/usb/mausb_host/include/utils/mausb_logs.h create mode 100644 drivers/usb/mausb_host/include/utils/mausb_mmap.h create mode 100644 drivers/usb/mausb_host/include/utils/mausb_ring_buffer.h create mode 100644 drivers/usb/mausb_host/include/utils/mausb_version.h create mode 100644 drivers/usb/mausb_host/src/hcd/hub.c create mode 100644 drivers/usb/mausb_host/src/hcd/module_init.c create mode 100644 drivers/usb/mausb_host/src/hcd/vhcd.c create mode 100644 drivers/usb/mausb_host/src/hpal/data_common.c create mode 100644 drivers/usb/mausb_host/src/hpal/data_in.c create mode 100644 drivers/usb/mausb_host/src/hpal/data_out.c create mode 100644 drivers/usb/mausb_host/src/hpal/hpal.c create mode 100644 drivers/usb/mausb_host/src/hpal/isoch_in.c create mode 100644 drivers/usb/mausb_host/src/hpal/isoch_out.c create mode 100644 drivers/usb/mausb_host/src/hpal/mausb_events.c create mode 100644 drivers/usb/mausb_host/src/hpal/network_callbacks.c create mode 100644 drivers/usb/mausb_host/src/link/mausb_ip_link.c create mode 100644 drivers/usb/mausb_host/src/utils/mausb_data_iterator.c create mode 100644 drivers/usb/mausb_host/src/utils/mausb_mmap.c create mode 100644 drivers/usb/mausb_host/src/utils/mausb_ring_buffer.c diff --git a/MAINTAINERS b/MAINTAINERS index 38fe2f3f7b6f..62b7d816187d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9946,6 +9946,12 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ S: Maintained F: drivers/media/dvb-frontends/m88rs2000* +MA USB HOST DRIVER +M: Vladimir Stankovic +W: https://www.displaylink.com/ +S: Maintained +F: drivers/usb/mausb_host/* + MA901 MASTERKIT USB FM RADIO DRIVER M: Alexey Klimov L: linux-media@vger.kernel.org diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 275568abc670..72ac47f6cb6c 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -164,6 +164,8 @@ source "drivers/usb/misc/Kconfig" source "drivers/usb/atm/Kconfig" +source "drivers/user/mausb_host/Kconfig" + endif # USB source "drivers/usb/phy/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 1c1c1d659394..22d1998db0e2 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -66,3 +66,5 @@ obj-$(CONFIG_USBIP_CORE) += usbip/ obj-$(CONFIG_TYPEC) += typec/ obj-$(CONFIG_USB_ROLE_SWITCH) += roles/ + +obj-$(CONFIG_HOST_MAUSB) += mausb_host/ diff --git a/drivers/usb/mausb_host/Kconfig b/drivers/usb/mausb_host/Kconfig new file mode 100644 index 000000000000..357229a4ebc3 --- /dev/null +++ b/drivers/usb/mausb_host/Kconfig @@ -0,0 +1,15 @@ +# +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. +# +# This file is subject to the terms and conditions of the GNU General Public +# License v2. See the file COPYING in the main directory of this archive for +# more details. +# + +config HOST_MAUSB + tristate "MA-USB Host Driver" + depends on USB=y + default y + help + This is a MA-USB Host driver which enables host communication + via MA-USB protocol stack. diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile new file mode 100644 index 000000000000..8ef3b8c44bbd --- /dev/null +++ b/drivers/usb/mausb_host/Makefile @@ -0,0 +1,30 @@ +# +# Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. +# +# This file is subject to the terms and conditions of the GNU General Public +# License v2. See the file COPYING in the main directory of this archive for +# more details. +# + +ccflags-$(CONFIG_HOST_MAUSB_DEBUG) := -DDEBUG + +obj-$(CONFIG_HOST_MAUSB) += host_mausb.o +host_mausb-y := src/hcd/hub.o +host_mausb-y += src/hcd/module_init.o +host_mausb-y += src/hcd/vhcd.o +host_mausb-y += src/hpal/data_common.o +host_mausb-y += src/hpal/data_in.o +host_mausb-y += src/hpal/data_out.o +host_mausb-y += src/hpal/hpal.o +host_mausb-y += src/hpal/isoch_in.o +host_mausb-y += src/hpal/isoch_out.o +host_mausb-y += src/hpal/mausb_events.o +host_mausb-y += src/hpal/network_callbacks.o +host_mausb-y += src/link/mausb_ip_link.o +host_mausb-y += src/utils/mausb_data_iterator.o +host_mausb-y += src/utils/mausb_mmap.o +host_mausb-y += src/utils/mausb_ring_buffer.o + +ccflags-y += -I$(srctree)/$(src)/include +ccflags-y += -g +ccflags-y += -DMAUSB_WITH_LOGS diff --git a/drivers/usb/mausb_host/include/common/ma_usb.h b/drivers/usb/mausb_host/include/common/ma_usb.h new file mode 100644 index 000000000000..72d602ebadc0 --- /dev/null +++ b/drivers/usb/mausb_host/include/common/ma_usb.h @@ -0,0 +1,873 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_COMMON_MA_USB_H__ +#define __MAUSB_COMMON_MA_USB_H__ + +#ifdef __KERNEL__ +#include +#else +#include +#endif /* __KERNEL__ */ + +#define MA_USB_SET_FIELD_(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, _v) +#define MA_USB_GET_FIELD_(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, _v) +#define MA_USB_SET_FIELD(_m_, _v) __mausb_set_field(MA_USB_##_m_##_MASK, \ + MA_USB_##_v) +#define MA_USB_GET_FIELD(_m_, _v) __mausb_get_field(MA_USB_##_m_##_MASK, \ + MA_USB_##_v) + +#define MA_USB_MGMT_TOKEN_RESERVED 0 +#define MA_USB_MGMT_TOKEN_MIN 1 +#define MA_USB_MGMT_TOKEN_MAX ((1 << 10) - 1) + +#define MA_USB_DATA_EPS_UNASSIGNED 0 +#define MA_USB_DATA_EPS_ACTIVE 1 +#define MA_USB_DATA_EPS_INACTIVE 2 +#define MA_USB_DATA_EPS_HALTED 3 + +#define MA_USB_DATA_TFLAGS_ARQ 1 +#define MA_USB_DATA_TFLAGS_NEG 2 +#define MA_USB_DATA_TFLAGS_EOT 4 +#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL 0 +#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH 8 +#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_BULK 16 +#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_INTR 24 + +#define MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK 0x18 + +#define MA_USB_DATA_IFLAGS_MTD_VALID 1 +#define MA_USB_DATA_IFLAGS_HDR_FMT_SHORT 0 +#define MA_USB_DATA_IFLAGS_HDR_FMT_STD 2 +#define MA_USB_DATA_IFLAGS_HDR_FMT_LONG 4 +#define MA_USB_DATA_IFLAGS_IRS_FMT_STD 0 +#define MA_USB_DATA_IFLAGS_IRS_FMT_LONG 2 +#define MA_USB_DATA_IFLAGS_ASAP 8 + +#define MA_USB_DATA_IFLAGS_FMT_MASK 0x6 + +/* version */ + +#define MA_USB_HDR_VERSION_1_0 0 + +/* flags */ + +#define MA_USB_HDR_FLAGS_HOST 1 +#define MA_USB_HDR_FLAGS_RETRY 2 +#define MA_USB_HDR_FLAGS_TIMESTAMP 4 +#define MA_USB_HDR_FLAGS_RESERVED 8 +#define MA_USB_HDR_FLAG(_f) MA_USB_HDR_FLAGS_##_f + +/* type and subtype */ + +#define MA_USB_HDR_TYPE_TYPE_MASK 0xC0 +#define MA_USB_HDR_TYPE_SUBTYPE_MASK 0x3F + +#define MA_USB_HDR_TYPE_TYPE_MANAGEMENT 0 +#define MA_USB_HDR_TYPE_TYPE_CONTROL 1 +#define MA_USB_HDR_TYPE_TYPE_DATA 2 + +/* Management subtypes */ + +#define _MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \ + MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2) +#define _MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \ + MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 1) + +#define MA_USB_HDR_TYPE_MANAGEMENT_REQ(_s) \ + _MA_USB_HDR_TYPE_MANAGEMENT_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s) +#define MA_USB_HDR_TYPE_MANAGEMENT_RESP(_s) \ + _MA_USB_HDR_TYPE_MANAGEMENT_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s) + +#define MA_USB_HDR_TYPE_SUBTYPE_CAP 0 +#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVHANDLE 1 +#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLE 2 +#define MA_USB_HDR_TYPE_SUBTYPE_EPACTIVATE 3 +#define MA_USB_HDR_TYPE_SUBTYPE_EPINACTIVATE 4 +#define MA_USB_HDR_TYPE_SUBTYPE_EPRESET 5 +#define MA_USB_HDR_TYPE_SUBTYPE_CLEARTRANSFERS 6 +#define MA_USB_HDR_TYPE_SUBTYPE_EPHANDLEDELETE 7 +#define MA_USB_HDR_TYPE_SUBTYPE_DEVRESET 8 +#define MA_USB_HDR_TYPE_SUBTYPE_MODIFYEP0 9 +#define MA_USB_HDR_TYPE_SUBTYPE_SETUSBDEVADDR 10 +#define MA_USB_HDR_TYPE_SUBTYPE_UPDATEDEV 11 +#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVDISCONNECT 12 +#define MA_USB_HDR_TYPE_SUBTYPE_USBSUSPEND 13 +#define MA_USB_HDR_TYPE_SUBTYPE_USBRESUME 14 +#define MA_USB_HDR_TYPE_SUBTYPE_REMOTEWAKE 15 +#define MA_USB_HDR_TYPE_SUBTYPE_PING 16 +#define MA_USB_HDR_TYPE_SUBTYPE_DEVDISCONNECT 17 +#define MA_USB_HDR_TYPE_SUBTYPE_DEVINITDISCONNECT 18 +#define MA_USB_HDR_TYPE_SUBTYPE_SYNCH 19 +#define MA_USB_HDR_TYPE_SUBTYPE_CANCELTRANSFER 20 +#define MA_USB_HDR_TYPE_SUBTYPE_EPOPENSTREAM 21 +#define MA_USB_HDR_TYPE_SUBTYPE_EPCLOSESTREAM 22 +#define MA_USB_HDR_TYPE_SUBTYPE_USBDEVRESET 23 +#define MA_USB_HDR_TYPE_SUBTYPE_DEVNOTIFICATION 24 +#define MA_USB_HDR_TYPE_SUBTYPE_EPSETKEEPALIVE 25 +#define MA_USB_HDR_TYPE_SUBTYPE_GETPORTBW 26 +#define MA_USB_HDR_TYPE_SUBTYPE_SLEEP 27 +#define MA_USB_HDR_TYPE_SUBTYPE_WAKE 28 +#define MA_USB_HDR_TYPE_SUBTYPE_VENDORSPECIFIC 31 /* Reserved */ + +/* Data subtypes */ + +#define _MA_USB_HDR_TYPE_DATA_REQ(_s) ({ \ + typeof(_s) (s) = (_s); \ + MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \ + MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 \ + + ((s) > 0 ? 1 : 0)); }) +#define _MA_USB_HDR_TYPE_DATA_RESP(_s) ({ \ + typeof(_s) (s) = (_s); \ + MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \ + MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (s) * 2 + 1 \ + + ((s) > 0 ? 1 : 0)); }) +#define _MA_USB_HDR_TYPE_DATA_ACK(_s) ( \ + MA_USB_SET_FIELD(HDR_TYPE_TYPE, HDR_TYPE_TYPE_DATA) | \ + MA_USB_SET_FIELD_(HDR_TYPE_SUBTYPE, (_s) * 2 + 2)) + +#define MA_USB_HDR_TYPE_DATA_REQ(_s) \ + _MA_USB_HDR_TYPE_DATA_REQ(MA_USB_HDR_TYPE_SUBTYPE_##_s) +#define MA_USB_HDR_TYPE_DATA_RESP(_s) \ + _MA_USB_HDR_TYPE_DATA_RESP(MA_USB_HDR_TYPE_SUBTYPE_##_s) +#define MA_USB_HDR_TYPE_DATA_ACK(_s) \ + _MA_USB_HDR_TYPE_DATA_ACK(MA_USB_HDR_TYPE_SUBTYPE_##_s) + +#define MA_USB_HDR_TYPE_SUBTYPE_TRANSFER 0 +#define MA_USB_HDR_TYPE_SUBTYPE_ISOCHTRANSFER 1 + +/* EP/Device Handle */ + +#define MA_USB_DEVICE_HANDLE_RESERVED 0 + +#define MA_USB_EP_HANDLE_D_MASK 0x0001 +#define MA_USB_EP_HANDLE_EP_N_MASK 0x001e +#define MA_USB_EP_HANDLE_ADDR_MASK 0x0fe0 +#define MA_USB_EP_HANDLE_BUS_N_MASK 0xf000 + +#define MA_USB_EP_HANDLE(_b, _a, _e, _d) ( \ + MA_USB_SET_FIELD_(EP_HANDLE_BUS_N, _b) | \ + MA_USB_SET_FIELD_(EP_HANDLE_ADDR, _a) | \ + MA_USB_SET_FIELD_(EP_HANDLE_EP_N, _e) | \ + MA_USB_SET_FIELD_(EP_HANDLE_D, _d)) + +#define MA_USB_EP_HANDLE_BUS_N_VIRTUAL 15 +#define MA_USB_EP_HANDLE_ADDR_DEFAULT 0 +#define MA_USB_EP_HANDLE_EP_N_DEFAULT 0 +#define MA_USB_EP_HANDLE_D_OUT 0 /* See USB2.0 9.3, Table 9-2 */ +#define MA_USB_EP_HANDLE_D_IN 1 /* See USB2.0 9.3, Table 9-2 */ + +/* Status codes */ + +#define MA_USB_HDR_STATUS_UNSUCCESSFUL -128 +#define MA_USB_HDR_STATUS_INVALID_MA_USB_SESSION_STATE -127 +#define MA_USB_HDR_STATUS_INVALID_DEVICE_HANDLE -126 +#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE -125 +#define MA_USB_HDR_STATUS_INVALID_EP_HANDLE_STATE -124 +#define MA_USB_HDR_STATUS_INVALID_REQUEST -123 +#define MA_USB_HDR_STATUS_MISSING_SEQUENCE_NUMBER -122 +#define MA_USB_HDR_STATUS_TRANSFER_PENDING -121 +#define MA_USB_HDR_STATUS_TRANSFER_EP_STALL -120 +#define MA_USB_HDR_STATUS_TRANSFER_SIZE_ERROR -119 +#define MA_USB_HDR_STATUS_TRANSFER_DATA_BUFFER_ERROR -118 +#define MA_USB_HDR_STATUS_TRANSFER_BABBLE_DETECTED -117 +#define MA_USB_HDR_STATUS_TRANSFER_TRANSACTION_ERROR -116 +#define MA_USB_HDR_STATUS_TRANSFER_SHORT_TRANSFER -115 +#define MA_USB_HDR_STATUS_TRANSFER_CANCELED -114 +#define MA_USB_HDR_STATUS_INSUFICIENT_RESOURCES -113 +#define MA_USB_HDR_STATUS_NOT_SUFFICIENT_BANDWIDTH -112 +#define MA_USB_HDR_STATUS_INTERNAL_ERROR -111 +#define MA_USB_HDR_STATUS_DATA_OVERRUN -110 +#define MA_USB_HDR_STATUS_DEVICE_NOT_ACCESSED -109 +#define MA_USB_HDR_STATUS_BUFFER_OVERRUN -108 +#define MA_USB_HDR_STATUS_BUSY -107 +#define MA_USB_HDR_STATUS_DROPPED_PACKET -106 +#define MA_USB_HDR_STATUS_ISOCH_TIME_EXPIRED -105 +#define MA_USB_HDR_STATUS_ISOCH_TIME_INVALID -104 +#define MA_USB_HDR_STATUS_NO_USB_PING_RESPONSE -103 +#define MA_USB_HDR_STATUS_NOT_SUPPORTED -102 +#define MA_USB_HDR_STATUS_REQUEST_DENIED -101 +#define MA_USB_HDR_STATUS_MISSING_REQUEST_ID -100 +#define MA_USB_HDR_STATUS_SUCCESS 0 /* Reserved */ +#define MA_USB_HDR_STATUS_NO_ERROR MA_USB_HDR_STATUS_SUCCESS /* Reserved */ + +/* Speed values */ + +#define MA_USB_SPEED_LOW_SPEED 0 +#define MA_USB_SPEED_FULL_SPEED 1 +#define MA_USB_SPEED_HIGH_SPEED 2 +#define MA_USB_SPEED_SUPER_SPEED 3 +#define MA_USB_SPEED_SUPER_SPEED_PLUS 4 + +/* capreq extra hdr */ + +#define MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH\ + (sizeof(struct ma_usb_desc) +\ + sizeof(struct ma_usb_capreq_desc_synchronization)) +#define MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH\ + (sizeof(struct ma_usb_desc) +\ + sizeof(struct ma_usb_capreq_desc_link_sleep)) + +#define MA_USB_CAPREQ_LENGTH\ + (sizeof(struct ma_usb_hdr_common) +\ + sizeof(struct ma_usb_hdr_capreq) +\ + MA_USB_CAPREQ_DESC_SYNCHRONIZATION_LENGTH +\ + MA_USB_CAPREQ_DESC_LINK_SLEEP_LENGTH) + +/* capreq desc types */ + +#define MA_USB_CAPREQ_DESC_TYPE_SYNCHRONIZATION 3 +#define MA_USB_CAPREQ_DESC_TYPE_LINK_SLEEP 5 + +/* capresp descriptors */ + +#define MA_USB_CAPRESP_DESC_TYPE_SPEED 0 +#define MA_USB_CAPRESP_DESC_TYPE_P_MANAGED_OUT 1 +#define MA_USB_CAPRESP_DESC_TYPE_ISOCHRONOUS 2 +#define MA_USB_CAPRESP_DESC_TYPE_SYNCHRONIZATION 3 +#define MA_USB_CAPRESP_DESC_TYPE_CONTAINER_ID 4 +#define MA_USB_CAPRESP_DESC_TYPE_LINK_SLEEP 5 +#define MA_USB_CAPRESP_DESC_TYPE_HUB_LATENCY 6 + +/* Request ID and sequence number values */ + +#define MA_USB_TRANSFER_RESERVED 0 +#define MA_USB_TRANSFER_REQID_MIN 0 +#define MA_USB_TRANSFER_REQID_MAX ((1 << 8) - 1) +#define MA_USB_TRANSFER_SEQN_MIN 0 +#define MA_USB_TRANSFER_SEQN_MAX ((1 << 24) - 2) +#define MA_USB_TRANSFER_SEQN_INVALID ((1 << 24) - 1) + +#define MA_USB_ISOCH_SFLAGS_FRAGMENT 0x1 +#define MA_USB_ISOCH_SFLAGS_LAST_FRAGMENT 0x2 + +#define MAUSB_MAX_MGMT_SIZE 50 + +#define MAUSB_TRANSFER_HDR_SIZE (u32)(sizeof(struct ma_usb_hdr_common) +\ + sizeof(struct ma_usb_hdr_transfer)) + +#define MAUSB_ISOCH_TRANSFER_HDR_SIZE (u16)(sizeof(struct ma_usb_hdr_common) +\ + sizeof(struct ma_usb_hdr_isochtransfer) +\ + sizeof(struct ma_usb_hdr_isochtransfer_optional)) + +#define MAX_ISOCH_ASAP_PACKET_SIZE (150000 /* Network MTU */ -\ + MAUSB_ISOCH_TRANSFER_HDR_SIZE - 20 /* IP header size */ -\ + 8 /* UDP header size*/) + +#define shift_ptr(ptr, offset) ((u8 *)(ptr) + (offset)) + +/* USB descriptor */ +struct ma_usb_desc { + u8 length; + u8 type; + u8 value[0]; +} __packed; + +struct ma_usb_ep_handle { + u16 d :1, + ep_n :4, + addr :7, + bus_n :4; +}; + +struct ma_usb_hdr_mgmt { + u32 status :8, + token :10, /* requestor originator allocated */ + reserved :14; +} __aligned(4); + +struct ma_usb_hdr_ctrl { /* used in all req/resp/conf operations */ + s8 status; + u8 link_type; + union { + u8 tid; /* ieee 802.11 */ + } connection_id; +} __aligned(4); + +struct ma_usb_hdr_data { + s8 status; + u8 eps :2, + t_flags :6; + union { + u16 stream_id; + struct { + u16 headers :12, + i_flags :4; + }; + }; +} __aligned(4); + +struct ma_usb_hdr_common { + u8 version :4, + flags :4; + u8 type; + u16 length; + union { + u16 dev; + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u8 dev_addr; + u8 ssid; + union { + s8 status; + struct ma_usb_hdr_mgmt mgmt; + struct ma_usb_hdr_ctrl ctrl; + struct ma_usb_hdr_data data; + }; +} __aligned(4); + +/* capreq extra hdr */ + +struct ma_usb_hdr_capreq { + u32 out_mgmt_reqs :12, + reserved :20; +} __aligned(4); + +struct ma_usb_capreq_desc_synchronization { + u8 media_time_available :1, + reserved :7; +} __packed; + +struct ma_usb_capreq_desc_link_sleep { + u8 link_sleep_capable :1, + reserved :7; +} __packed; + +/* capresp extra hdr */ + +struct ma_usb_hdr_capresp { + u16 endpoints; + u8 devices; + u8 streams :5, + dev_type :3; + u32 descs :8, + descs_length :24; + u16 out_transfer_reqs; + u16 out_mgmt_reqs :12, + reserved :4; +} __aligned(4); + +struct ma_usb_capresp_desc_speed { + u8 reserved1 :4, + speed :4; + u8 reserved2 :4, + lse :2, /* USB3.1 8.5.6.7, Table 8-22 */ + reserved3 :2; +} __packed; + +struct ma_usb_capresp_desc_p_managed_out { + u8 elastic_buffer :1, + drop_notification :1, + reserved :6; +} __packed; + +struct ma_usb_capresp_desc_isochronous { + u8 payload_dword_aligned :1, + reserved :7; +} __packed; + +struct ma_usb_capresp_desc_synchronization { + u8 media_time_available :1, + time_stamp_required :1,/* hubs need this set */ + reserved :6; +} __packed; + +struct ma_usb_capresp_desc_container_id { + u8 container_id[16]; /* UUID IETF RFC 4122 */ +} __packed; + +struct ma_usb_capresp_desc_link_sleep { + u8 link_sleep_capable :1, + reserved :7; +} __packed; + +struct ma_usb_capresp_desc_hub_latency { + u16 latency; /* USB3.1 */ +} __packed; + +/* usbdevhandlereq extra hdr */ +struct ma_usb_hdr_usbdevhandlereq { + u32 route_string :20, + speed :4, + reserved1 :8; + u16 hub_dev_handle; + u16 reserved2; + u16 parent_hs_hub_dev_handle; + u16 parent_hs_hub_port :4, + mtt :1, /* USB2.0 11.14, 11.14.1.3 */ + lse :2, /* USB3.1 8.5.6.7, Table 8-22 */ + reserved3 :9; +} __aligned(4); + +/* usbdevhandleresp extra hdr */ +struct ma_usb_hdr_usbdevhandleresp { + u16 dev_handle; + u16 reserved; +} __aligned(4); + +/* ephandlereq extra hdr */ +struct ma_usb_hdr_ephandlereq { + u32 ep_descs :5, + ep_desc_size :6, + reserved :21; +} __aligned(4); + +/* + * Restricted USB2.0 ep desc limited to 6 bytes, isolating further changes. + * See USB2.0 9.6.6, Table 9-13 + */ +struct usb_ep_desc { + u8 bLength; + /* USB2.0 9.4, Table 9-5 (5) usb/ch9.h: USB_DT_ENDPOINT */ + u8 bDescriptorType; + u8 bEndpointAddress; + u8 bmAttributes; + __le16 wMaxPacketSize; + u8 bInterval; +} __packed; + +/* + * Restricted USB3.1 ep comp desc isolating further changes in usb/ch9.h + * See USB3.1 9.6.7, Table 9-26 + */ +struct usb_ss_ep_comp_desc { + u8 bLength; + /* USB3.1 9.4, Table 9-6 (48) usb/ch9.h: USB_DT_SS_ENDPOINT_COMP */ + u8 bDescriptorType; + u8 bMaxBurst; + u8 bmAttributes; + __le16 wBytesPerInterval; +} __packed; + +/* + * USB3.1 ss_plus_isoch_ep_comp_desc + * See USB3.1 9.6.8, Table 9-27 + */ +struct usb_ss_plus_isoch_ep_comp_desc { + u8 bLength; + /* USB3.1 9.4, Table 9-6 (49) usb/ch9.h: not yet defined! */ + u8 bDescriptorType; + u16 wReserved; + u32 dwBytesPerInterval; +} __packed; + +struct ma_usb_ephandlereq_desc_std { + struct usb_ep_desc usb20; +} __aligned(4); + +struct ma_usb_ephandlereq_desc_ss { + struct usb_ep_desc usb20; + struct usb_ss_ep_comp_desc usb31; +} __aligned(4); + +struct ma_usb_ephandlereq_desc_ss_plus { + struct usb_ep_desc usb20; + struct usb_ss_ep_comp_desc usb31; + struct usb_ss_plus_isoch_ep_comp_desc usb31_isoch; +} __aligned(4); + +struct ma_usb_dev_context { + struct usb_ep_desc usb; +}; + +/* ephandleresp extra hdr */ +struct ma_usb_hdr_ephandleresp { + u32 ep_descs :5, + reserved :27; +} __aligned(4); + +/* ephandleresp descriptor */ +struct ma_usb_ephandleresp_desc { + union { + struct ma_usb_ep_handle eph; + u16 epv; + } ep_handle; + u16 d :1, /* non-control or non-OUT */ + isoch :1, + l_managed :1, /* control or non-isoch OUT */ + invalid :1, + reserved1 :12; + u16 ccu; /* control or non-isoch OUT */ + u16 reserved2; + u32 buffer_size; /* control or OUT */ + u16 isoch_prog_delay; /* in us. */ + u16 isoch_resp_delay; /* in us. */ +} __aligned(4); + +/* epactivatereq extra hdr */ +struct ma_usb_hdr_epactivatereq { + u32 ep_handles :5, + reserved :27; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epactivateresp extra hdr */ +struct ma_usb_hdr_epactivateresp { + u32 ep_errors :5, + reserved :27; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epinactivatereq extra hdr */ +struct ma_usb_hdr_epinactivatereq { + u32 ep_handles :5, + suspend :1, + reserved :26; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epinactivateresp extra hdr */ +struct ma_usb_hdr_epinactivateresp { + u32 ep_errors :5, + reserved :27; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epresetreq extra hdr */ +struct ma_usb_hdr_epresetreq { + u32 ep_reset_blocks :5, + reserved :27; +} __aligned(4); + +/* epresetreq reset block */ +struct ma_usb_epresetreq_block { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u16 tsp :1, + reserved :15; +} __aligned(4); + +/* epresetresp extra hdr */ +struct ma_usb_hdr_epresetresp { + u32 ep_errors :5, + reserved :27; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* cleartransfersreq extra hdr */ +struct ma_usb_hdr_cleartransfersreq { + u32 info_blocks :8, + reserved :24; +} __aligned(4); + +/* cleartransfersreq info block */ +struct ma_usb_cleartransfersreq_block { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u16 stream_id; /* ss stream eps only */ + u32 start_req_id :8, + reserved :24; +} __aligned(4); + +/* cleartransfersresp extra hdr */ +struct ma_usb_hdr_cleartransfersresp { + u32 status_blocks :8, + reserved :24; +} __aligned(4); + +/* cleartransfersresp status block */ +struct ma_usb_cleartransfersresp_block { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u16 stream_id; /* ss stream eps only */ + u32 cancel_success :1, + partial_delivery :1, + reserved :30; + u32 last_req_id :8, + delivered_seq_n :24; /* OUT w/partial_delivery only */ + u32 delivered_byte_offset; /* OUT w/partial_delivery only */ +} __aligned(4); + +/* ephandledeletereq extra hdr */ +struct ma_usb_hdr_ephandledeletereq { + u32 ep_handles :5, + reserved :27; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* ephandledeleteresp extra hdr */ +struct ma_usb_hdr_ephandledeleteresp { + u32 ep_errors :5, + reserved :27; + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* modifyep0req extra hdr */ +struct ma_usb_hdr_modifyep0req { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u16 max_packet_size; +} __aligned(4); + +/* + * modifyep0resp extra hdr + * Only if req ep0 handle addr was 0 and req dev is in the addressed state + * or if req ep0 handle addr != 0 and req dev is in default state + */ +struct ma_usb_hdr_modifyep0resp { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + + u16 reserved; +} __aligned(4); + +/* setusbdevaddrreq extra hdr */ +struct ma_usb_hdr_setusbdevaddrreq { + u16 response_timeout; /* in ms. */ + u16 reserved; +} __aligned(4); + +/* setusbdevaddrresp extra hdr */ +struct ma_usb_hdr_setusbdevaddrresp { + u32 addr :7, + reserved :25; +} __aligned(4); + +/* updatedevreq extra hdr */ +struct ma_usb_hdr_updatedevreq { + u16 max_exit_latency; /* hubs only */ + u8 hub :1, + ports :4, + mtt :1, + ttt :2; + u8 integrated_hub_latency :1, + reserved :7; +} __aligned(4); + +/* + * USB2.0 dev desc, isolating further changes in usb/ch9.h + * See USB2.0 9.6.6, Table 9-13 + */ +struct usb_dev_desc { + u8 bLength; + /* + * USB2.0 9.4, Table 9-5 (1) + * usb/ch9.h: USB_DT_DEVICE + */ + u8 bDescriptorType; + __le16 bcdUSB; + u8 bDeviceClass; + u8 bDeviceSubClass; + u8 bDeviceProtocol; + u8 bMaxPacketSize0; + __le16 idVendor; + __le16 idProduct; + __le16 bcdDevice; + u8 iManufacturer; + u8 iProduct; + u8 iSerialNumber; + u8 bNumConfigurations; +} __packed; + +struct ma_usb_updatedevreq_desc { + struct usb_dev_desc usb20; +} __aligned(4); + +/* remotewakereq extra hdr */ +struct ma_usb_hdr_remotewakereq { + u32 resumed :1, + reserved :31; +} __aligned(4); + +/* synchreq/resp extra hdr */ +struct ma_usb_hdr_synch { + u32 mtd_valid :1, /* MA-USB1.0b 6.5.1.8, Table 66 */ + resp_required :1, + reserved :30; + union { + u32 timestamp; /* MA-USB1.0b 6.5.1.11 */ + struct { + u32 delta :13, + bus_interval :19; + }; /* MA-USB1.0b 6.6.1, Table 69 */ + }; + u32 mtd; /* MA-USB1.0b 6.5.1.12 */ +} __aligned(4); + +/* canceltransferreq extra hdr */ +struct ma_usb_hdr_canceltransferreq { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u16 stream_id; + u32 req_id :8, + reserved :24; +} __aligned(4); + +/* canceltransferresp extra hdr */ +struct ma_usb_hdr_canceltransferresp { + union { + u16 epv; + struct ma_usb_ep_handle eph; + } handle; + u16 stream_id; + u32 req_id :8, + cancel_status :3, + reserved1 :21; + u32 delivered_seq_n :24, + reserved2 :8; + u32 delivered_byte_offset; +} __aligned(4); + +/* transferreq/resp/ack extra hdr */ +struct ma_usb_hdr_transfer { + u32 seq_n :24, + req_id :8; + union { + u32 rem_size_credit; + /* ISOCH aliased fields added for convenience. */ + struct { + u32 presentation_time :20, + segments :12; + }; + }; +} __aligned(4); + +/* isochtransferreq/resp extra hdr */ +struct ma_usb_hdr_isochtransfer { + u32 seq_n :24, + req_id :8; + u32 presentation_time :20, + segments :12; +} __aligned(4); + +/* isochtransferreq/resp optional hdr */ +struct ma_usb_hdr_isochtransfer_optional { + union { + u32 timestamp; /* MA-USB1.0b 6.5.1.11 */ + struct { + u32 delta :13, + bus_interval :19; + }; /* MA-USB1.0b 6.6.1, Table 69 */ + }; + u32 mtd; /* MA-USB1.0b 6.5.1.12 */ +} __aligned(4); + +/* isochdatablock hdrs */ + +struct ma_usb_hdr_isochdatablock_short { + u16 block_length; + u16 segment_number :12, + s_flags :4; +} __aligned(4); + +struct ma_usb_hdr_isochdatablock_std { + u16 block_length; + u16 segment_number :12, + s_flags :4; + u16 segment_length; + u16 fragment_offset; +} __aligned(4); + +struct ma_usb_hdr_isochdatablock_long { + u16 block_length; + u16 segment_number :12, + s_flags :4; + u32 segment_length; + u32 fragment_offset; +} __aligned(4); + +/* isochreadsizeblock hdrs */ + +struct ma_usb_hdr_isochreadsizeblock_std { + u32 service_intervals :12, + max_segment_length :20; +} __aligned(4); + +struct ma_usb_hdr_isochreadsizeblock_long { + u32 service_intervals :12, + reserved :20; + u32 max_segment_length; +} __aligned(4); + +static inline int __mausb_set_field(int m, int v) +{ + return ((~((m) - 1) & (m)) * (v)) & (m); +} + +static inline int __mausb_get_field(int m, int v) +{ + return ((v) & (m)) / (~((m) - 1) & (m)); +} + +static inline bool mausb_is_management_hdr_type(int hdr_type) +{ + return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type) + == MA_USB_HDR_TYPE_TYPE_MANAGEMENT; +} + +static inline bool mausb_is_data_hdr_type(int hdr_type) +{ + return MA_USB_GET_FIELD_(HDR_TYPE_TYPE, hdr_type) + == MA_USB_HDR_TYPE_TYPE_DATA; +} + +static inline bool mausb_is_management_resp_hdr_type(int hdr_resp_type) +{ + return mausb_is_management_hdr_type(hdr_resp_type) && + (MA_USB_GET_FIELD_(HDR_TYPE_SUBTYPE, hdr_resp_type) & 1) + != 0; +} + +static inline +struct ma_usb_hdr_transfer * +mausb_get_data_transfer_hdr(struct ma_usb_hdr_common *hdr) +{ + return (struct ma_usb_hdr_transfer *)shift_ptr(hdr, sizeof(*hdr)); +} + +static inline +struct ma_usb_hdr_isochtransfer * +mausb_get_isochtransfer_hdr(struct ma_usb_hdr_common *hdr) +{ + return (struct ma_usb_hdr_isochtransfer *)shift_ptr(hdr, sizeof(*hdr)); +} + +static inline +struct ma_usb_hdr_isochtransfer_optional * +mausb_hdr_isochtransfer_optional_hdr(struct ma_usb_hdr_common *hdr) +{ + return (struct ma_usb_hdr_isochtransfer_optional *) + shift_ptr(hdr, sizeof(struct ma_usb_hdr_common) + + sizeof(struct ma_usb_hdr_isochtransfer)); +} + +#endif /* __MAUSB_COMMON_MA_USB_H__ */ diff --git a/drivers/usb/mausb_host/include/common/mausb_address.h b/drivers/usb/mausb_host/include/common/mausb_address.h new file mode 100644 index 000000000000..03997be33391 --- /dev/null +++ b/drivers/usb/mausb_host/include/common/mausb_address.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_COMMON_MAUSB_ADDRESS_H__ +#define __MAUSB_COMMON_MAUSB_ADDRESS_H__ + +#ifdef __KERNEL__ +#include +#else +#include +#endif /* __KERNEL__ */ + +#define ADDR_LEN 16 + +struct mausb_device_address { + u8 link_type; + struct { + union { + char ip4[ADDR_LEN]; + u8 ip6[ADDR_LEN]; + } address; + u8 number_of_ports; + struct { + u16 management; + u16 control; + u16 bulk; + u16 interrupt; + u16 isochronous; + } port; + } ip; +}; + +#endif /* __MAUSB_COMMON_MAUSB_ADDRESS_H__ */ diff --git a/drivers/usb/mausb_host/include/common/mausb_driver_status.h b/drivers/usb/mausb_host/include/common/mausb_driver_status.h new file mode 100644 index 000000000000..e4f42e6df058 --- /dev/null +++ b/drivers/usb/mausb_host/include/common/mausb_driver_status.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_COMMON_MAUSB_DRIVER_STATUS_H__ +#define __MAUSB_COMMON_MAUSB_DRIVER_STATUS_H__ + +#define MAUSB_DRIVER_BAD_READ_BUFFER_SIZE -1 +#define MAUSB_DRIVER_RING_EVENTS_STOPPED -2 +#define MAUSB_DRIVER_SYSTEM_SUSPENDED -3 +#define MAUSB_DRIVER_COPY_TO_BUFFER_FAILED -4 + +#define MAUSB_DRIVER_READ_TIMEOUT 0 +#define MAUSB_DRIVER_READ_ERROR -1 +#define MAUSB_DRIVER_WRITE_ERROR -2 + +#endif diff --git a/drivers/usb/mausb_host/include/common/mausb_event.h b/drivers/usb/mausb_host/include/common/mausb_event.h new file mode 100644 index 000000000000..1c117fd37d20 --- /dev/null +++ b/drivers/usb/mausb_host/include/common/mausb_event.h @@ -0,0 +1,228 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_COMMON_MAUSB_EVENT_H__ +#define __MAUSB_COMMON_MAUSB_EVENT_H__ + +#include + +#define MAUSB_MAX_NUM_OF_MA_DEVS 15 +#define MAUSB_RING_BUFFER_SIZE 1024 +#define MAUSB_MAX_DATA_IN_REQ_SIZE 28 + +#define MAUSB_EVENT_TYPE_DEV_RESET 1u +#define MAUSB_EVENT_TYPE_USB_DEV_HANDLE 2u +#define MAUSB_EVENT_TYPE_EP_HANDLE 3u +#define MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE 4u +#define MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE 5u +#define MAUSB_EVENT_TYPE_EP_HANDLE_RESET 6u +#define MAUSB_EVENT_TYPE_EP_HANDLE_DELETE 7u +#define MAUSB_EVENT_TYPE_MODIFY_EP0 8u +#define MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS 9u +#define MAUSB_EVENT_TYPE_UPDATE_DEV 10u +#define MAUSB_EVENT_TYPE_USB_DEV_DISCONNECT 11u +#define MAUSB_EVENT_TYPE_PING 12u +#define MAUSB_EVENT_TYPE_DEV_DISCONNECT 13u +#define MAUSB_EVENT_TYPE_USB_DEV_RESET 14u +#define MAUSB_EVENT_TYPE_CANCEL_TRANSFER 15u + +#define MAUSB_EVENT_TYPE_PORT_CHANGED 80u +#define MAUSB_EVENT_TYPE_SEND_MGMT_MSG 81u +#define MAUSB_EVENT_TYPE_SEND_DATA_MSG 82u +#define MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG 83u +#define MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG 84u +#define MAUSB_EVENT_TYPE_URB_COMPLETE 85u +#define MAUSB_EVENT_TYPE_SEND_ACK 86u +#define MAUSB_EVENT_TYPE_ITERATOR_RESET 87u +#define MAUSB_EVENT_TYPE_ITERATOR_SEEK 88u +#define MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER 89u +#define MAUSB_EVENT_TYPE_DELETE_MA_DEV 90u +#define MAUSB_EVENT_TYPE_USER_FINISHED 91u +#define MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES 92u +#define MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED 93u +#define MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT 94u + +#define MAUSB_EVENT_TYPE_NONE 255u + +#define MAUSB_DATA_MSG_DIRECTION_OUT 0 +#define MAUSB_DATA_MSG_DIRECTION_IN 1 +#define MAUSB_DATA_MSG_CONTROL MAUSB_DATA_MSG_DIRECTION_OUT + +struct mausb_devhandle { + u64 event_id; + u32 route_string; + u16 hub_dev_handle; + u16 parent_hs_hub_dev_handle; + u16 parent_hs_hub_port; + u16 mtt; + /* dev_handle assigned in user */ + u16 dev_handle; + u8 device_speed; + u8 lse; +}; + +struct mausb_ephandle { + u64 event_id; + u16 device_handle; + u16 descriptor_size; + /* ep_handle assigned in user */ + u16 ep_handle; + char descriptor[sizeof(struct ma_usb_ephandlereq_desc_ss)]; +}; + +struct mausb_epactivate { + u64 event_id; + u16 device_handle; + u16 ep_handle; +}; + +struct mausb_epinactivate { + u64 event_id; + u16 device_handle; + u16 ep_handle; +}; + +struct mausb_epreset { + u64 event_id; + u16 device_handle; + u16 ep_handle; + u8 tsp; +}; + +struct mausb_epdelete { + u64 event_id; + u16 device_handle; + u16 ep_handle; +}; + +struct mausb_updatedev { + u64 event_id; + u16 device_handle; + u16 max_exit_latency; + struct ma_usb_updatedevreq_desc update_descriptor; + u8 hub; + u8 number_of_ports; + u8 mtt; + u8 ttt; + u8 integrated_hub_latency; +}; + +struct mausb_usbdevreset { + u64 event_id; + u16 device_handle; +}; + +struct mausb_modifyep0 { + u64 event_id; + u16 device_handle; + u16 ep_handle; + __le16 max_packet_size; +}; + +struct mausb_setusbdevaddress { + u64 event_id; + u16 device_handle; + u16 response_timeout; +}; + +struct mausb_usbdevdisconnect { + u16 device_handle; +}; + +struct mausb_canceltransfer { + u64 urb; + u16 device_handle; + u16 ep_handle; +}; + +struct mausb_mgmt_hdr { + __aligned(4) char hdr[MAUSB_MAX_MGMT_SIZE]; +}; + +struct mausb_mgmt_req_timedout { + u64 event_id; +}; + +struct mausb_delete_ma_dev { + u64 event_id; + u16 device_id; +}; + +/* TODO split mgmt_event to generic send mgmt req and specific requests */ +struct mausb_mgmt_event { + union { + struct mausb_devhandle dev_handle; + struct mausb_ephandle ep_handle; + struct mausb_epactivate ep_activate; + struct mausb_epinactivate ep_inactivate; + struct mausb_epreset ep_reset; + struct mausb_epdelete ep_delete; + struct mausb_modifyep0 modify_ep0; + struct mausb_setusbdevaddress set_usb_dev_address; + struct mausb_updatedev update_dev; + struct mausb_usbdevreset usb_dev_reset; + struct mausb_usbdevdisconnect usb_dev_disconnect; + struct mausb_canceltransfer cancel_transfer; + struct mausb_mgmt_hdr mgmt_hdr; + struct mausb_mgmt_req_timedout mgmt_req_timedout; + struct mausb_delete_ma_dev delete_ma_dev; + }; +}; + +struct mausb_data_event { + u64 urb; + u64 recv_buf; + u32 iterator_seek_delta; + u32 transfer_size; + u32 rem_transfer_size; + u32 transfer_flags; + u32 isoch_seg_num; + u32 req_id; + u32 payload_size; + s32 status; + + __aligned(4) char hdr[MAUSB_TRANSFER_HDR_SIZE]; + __aligned(4) char hdr_ack[MAUSB_TRANSFER_HDR_SIZE]; + + u16 device_id; + u16 ep_handle; + u16 packet_size; + u8 setup_packet; + u8 direction; + u8 transfer_type; + u8 first_control_packet; + u8 transfer_eot; + u8 mausb_address; + u8 mausb_ssid; +}; + +struct mausb_port_changed_event { + u8 port; + u8 dev_type; + u8 dev_speed; + u8 lse; +}; + +struct mausb_event { + union { + struct mausb_mgmt_event mgmt; + struct mausb_data_event data; + struct mausb_port_changed_event port_changed; + }; + s32 status; + u8 type; + u8 madev_addr; +}; + +struct mausb_events_notification { + u16 num_of_events; + u16 num_of_completed_events; + u8 madev_addr; +}; + +#endif /* __MAUSB_COMMON_MAUSB_EVENT_H__ */ diff --git a/drivers/usb/mausb_host/include/hcd/hub.h b/drivers/usb/mausb_host/include/hcd/hub.h new file mode 100644 index 000000000000..ca834666525a --- /dev/null +++ b/drivers/usb/mausb_host/include/hcd/hub.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HCD_HUB_H__ +#define __MAUSB_HCD_HUB_H__ + +#include +#include + +#include "utils/mausb_data_iterator.h" + +#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 mausb_data_iter iterator; + struct rb_node rb_node; + struct work_struct work; +}; + +int mausb_probe(struct device *dev); +void mausb_hcd_urb_complete(struct urb *urb, u32 actual_length, int status); + +#ifdef ISOCH_CALLBACKS +int mausb_sec_event_ring_setup(struct usb_hcd *hcd, unsigned int intr_num); +int mausb_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned int intr_num); +phys_addr_t mausb_get_sec_event_ring_phys_addr(struct usb_hcd *hcd, + unsigned int intr_num, + dma_addr_t *dma); +phys_addr_t mausb_get_xfer_ring_phys_addr(struct usb_hcd *hcd, + struct usb_device *udev, + struct usb_host_endpoint *ep, + dma_addr_t *dma); +int mausb_get_core_id(struct usb_hcd *hcd); +#endif /* ISOCH_CALLBACKS */ + +void mausb_clear_hcd_madev(u16 port_number); + +#endif /* __MAUSB_HCD_HUB_H__ */ diff --git a/drivers/usb/mausb_host/include/hcd/vhcd.h b/drivers/usb/mausb_host/include/hcd/vhcd.h new file mode 100644 index 000000000000..6434e7c60a18 --- /dev/null +++ b/drivers/usb/mausb_host/include/hcd/vhcd.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HCD_VHCD_H__ +#define __MAUSB_HCD_VHCD_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include "utils/mausb_ring_buffer.h" + +#define DEVICE_NAME "mausb_host_hcd_dev" +#define CLASS_NAME "mausb" + +#define NUMBER_OF_PORTS 15 + +#define MAX_USB_DEVICE_DEPTH 6 + +#define RESPONSE_TIMEOUT 5000 + +enum mausb_device_type { + USBDEVICE = 0, + USB20HUB = 1, + USB30HUB = 2 +}; + +enum mausb_device_speed { + LOW_SPEED = 0, + FULL_SPEED = 1, + HIGH_SPEED = 2, + SUPER_SPEED = 3, + SUPER_SPEED_PLUS = 4 +}; + +struct mausb_hcd { + spinlock_t lock; /* Protect HCD during URB processing */ + struct device *pdev; + u16 connected_ports; + + struct rb_root mausb_urbs; + struct hub_ctx *hcd_ss_ctx; + struct hub_ctx *hcd_hs_ctx; + struct notifier_block power_state_listener; +}; + +struct mausb_dev { + u32 port_status; + struct rb_root usb_devices; + u8 dev_speed; + void *ma_dev; +}; + +struct hub_ctx { + struct mausb_hcd *mhcd; + struct usb_hcd *hcd; + struct mausb_dev ma_devs[NUMBER_OF_PORTS]; +}; + +int mausb_init_hcd(void); +void mausb_deinit_hcd(void); + +void mausb_port_has_changed(const enum mausb_device_type device_type, + const enum mausb_device_speed device_speed, + void *ma_dev); +void mausb_hcd_disconnect(const u16 port_number, + const enum mausb_device_type device_type, + const enum mausb_device_speed device_speed); + +#endif /* __MAUSB_HCD_VHCD_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/data_common.h b/drivers/usb/mausb_host/include/hpal/data_common.h new file mode 100644 index 000000000000..8803b7b7782b --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/data_common.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_DATA_COMMON_H__ +#define __MAUSB_HPAL_DATA_COMMON_H__ + +#include "hpal/hpal.h" + +int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num, + struct mausb_kvec_data_wrapper *data); + +int mausb_send_transfer_ack(struct mausb_device *dev, + struct mausb_event *event); + +int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event); + +int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event); + +static inline bool mausb_ctrl_transfer(struct ma_usb_hdr_common *hdr) +{ + return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) == + MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL; +} + +static inline bool mausb_isoch_transfer(struct ma_usb_hdr_common *hdr) +{ + return (hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK) == + MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH; +} + +static inline bool mausb_ctrl_data_event(struct mausb_event *event) +{ + return event->data.transfer_type == + MA_USB_DATA_TFLAGS_TRANSFER_TYPE_CTRL; +} + +static inline bool mausb_isoch_data_event(struct mausb_event *event) +{ + return event->data.transfer_type == + MA_USB_DATA_TFLAGS_TRANSFER_TYPE_ISOCH; +} + +/* usb to mausb transfer type */ +static inline +u8 mausb_transfer_type_from_usb(struct usb_endpoint_descriptor *epd) +{ + return (u8)usb_endpoint_type(epd) << 3; +} + +static inline u8 mausb_transfer_type_from_hdr(struct ma_usb_hdr_common *hdr) +{ + return hdr->data.t_flags & MA_USB_DATA_TFLAGS_TRANSFER_TYPE_MASK; +} + +static inline +enum mausb_channel mausb_transfer_type_to_channel(u8 transfer_type) +{ + return transfer_type >> 3; +} + +#endif /* __MAUSB_HPAL_DATA_COMMON_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/data_in.h b/drivers/usb/mausb_host/include/hpal/data_in.h new file mode 100644 index 000000000000..a8bb225cde80 --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/data_in.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_DATA_IN_H__ +#define __MAUSB_HPAL_DATA_IN_H__ + +#include "hpal/hpal.h" + +int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event); +void mausb_receive_in_data(struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); + +#endif /* __MAUSB_HPAL_DATA_IN_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/data_out.h b/drivers/usb/mausb_host/include/hpal/data_out.h new file mode 100644 index 000000000000..d4d55a63704a --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/data_out.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_DATA_OUT_H__ +#define __MAUSB_HPAL_DATA_OUT_H__ + +#include + +#include "hpal/mausb_events.h" +#include "utils/mausb_data_iterator.h" + +int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); +void mausb_receive_out_data(struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); + +#endif /* __MAUSB_HPAL_DATA_OUT_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/hpal.h b/drivers/usb/mausb_host/include/hpal/hpal.h new file mode 100644 index 000000000000..8436e8eeb4c8 --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/hpal.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_HPAL_H__ +#define __MAUSB_HPAL_HPAL_H__ + +#include +#include +#include + +#include "common/mausb_address.h" +#include "common/mausb_event.h" +#include "hcd/hub.h" +#include "link/mausb_ip_link.h" + +#define MAUSB_CONTROL_SETUP_SIZE 8 +#define MAUSB_BUSY_RETRIES_COUNT 3 +#define MAUSB_HEARTBEAT_TIMEOUT_MS 1000 +#define MAUSB_CLIENT_STOPPED_TIMEOUT_MS 3000 + +#define MAUSB_MAX_RECEIVE_FAILURES 3 +#define MAUSB_MAX_MISSED_HEARTBEATS 3 +#define MAUSB_TRANSFER_RESERVED 0 + +#define MAUSB_CHANNEL_MAP_LENGTH 4 + +extern struct mss mss; +extern struct mausb_hcd *mhcd; + +enum mausb_isoch_header_format_size { + MAUSB_ISOCH_SHORT_FORMAT_SIZE = 4, + MAUSB_ISOCH_STANDARD_FORMAT_SIZE = 8, + MAUSB_ISOCH_LONG_FORMAT_SIZE = 12 +}; + +struct mausb_completion { + struct list_head list_entry; + struct completion *completion_event; + struct mausb_event *mausb_event; + u64 event_id; +}; + +struct mausb_mss_rings_events { + atomic_t mausb_stop_reading_ring_events; + struct completion mausb_ring_has_events; +}; + +struct mss { + bool deinit_in_progress; + spinlock_t lock; /* Protect mss structure */ + u64 ring_buffer_id; + + struct completion empty; + struct completion client_stopped; + bool client_connected; + struct timer_list heartbeat_timer; + u8 missed_heartbeats; + + struct list_head madev_list; + atomic_t num_of_transitions_to_sleep; + struct list_head available_ring_buffers; + + struct mausb_mss_rings_events rings_events; + struct mausb_events_notification events[MAUSB_MAX_NUM_OF_MA_DEVS]; +}; + +struct mausb_device { + struct mausb_device_address dev_addr; + struct net *net_ns; + struct mausb_ring_buffer *ring_buffer; + struct list_head list_entry; + + struct mausb_ip_ctx *mgmt_channel; + struct mausb_ip_ctx *ctrl_channel; + struct mausb_ip_ctx *bulk_channel; + struct mausb_ip_ctx *isoch_channel; + struct mausb_ip_ctx *channel_map[MAUSB_CHANNEL_MAP_LENGTH]; + + struct work_struct work; + struct work_struct socket_disconnect_work; + struct work_struct hcd_disconnect_work; + struct work_struct madev_delete_work; + struct work_struct ping_work; + struct work_struct heartbeat_work; + struct workqueue_struct *workq; + + struct kref refcount; + /* Set on port change event after cap resp */ + u8 dev_type; + u8 dev_speed; + u8 lse; + u8 madev_addr; + u8 dev_connected; + u16 id; + u16 port_number; + + u64 event_id; + spinlock_t event_id_lock; /* Lock event ID increments */ + + struct list_head completion_events; + spinlock_t completion_events_lock; /* Lock completion events */ + + struct completion user_finished_event; + u16 num_of_user_events; + u16 num_of_completed_events; + + spinlock_t num_of_user_events_lock; /* Lock user events count */ + + struct timer_list connection_timer; + u8 receive_failures_num; + spinlock_t connection_timer_lock; /* Lock connection timer */ + + atomic_t unresponsive_client; + + atomic_t num_of_usb_devices; +}; + +struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb); +struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb, + int status); +struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr); + +static inline u64 mausb_event_id(struct mausb_device *dev) +{ + unsigned long flags; + u64 val; + + spin_lock_irqsave(&dev->event_id_lock, flags); + val = ++(dev->event_id); + spin_unlock_irqrestore(&dev->event_id_lock, flags); + + return val; +} + +int mausb_initiate_dev_connection(struct mausb_device_address device_address, + u8 madev_address); +int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events, + u16 num_of_completed); +int mausb_enqueue_event_to_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle, + struct urb *request); +int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event, + u64 event_id); +int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep); + +static inline void mausb_insert_event(struct mausb_device *dev, + struct mausb_completion *event) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->completion_events_lock, flags); + list_add_tail(&event->list_entry, &dev->completion_events); + spin_unlock_irqrestore(&dev->completion_events_lock, flags); +} + +static inline void mausb_remove_event(struct mausb_device *dev, + struct mausb_completion *event) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->completion_events_lock, flags); + list_del(&event->list_entry); + spin_unlock_irqrestore(&dev->completion_events_lock, flags); +} + +void mausb_release_ma_dev_async(struct kref *kref); +void mausb_on_madev_connected(struct mausb_device *dev); +void mausb_complete_request(struct urb *urb, u32 actual_length, int status); +void mausb_complete_urb(struct mausb_event *event); +void mausb_reset_connection_timer(struct mausb_device *dev); +void mausb_reset_heartbeat_cnt(void); +void mausb_release_event_resources(struct mausb_event *event); +void mausb_initialize_mss(void); +void mausb_deinitialize_mss(void); +int mausb_register_power_state_listener(void); +void mausb_unregister_power_state_listener(void); + +#endif /* __MAUSB_HPAL_HPAL_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/isoch_in.h b/drivers/usb/mausb_host/include/hpal/isoch_in.h new file mode 100644 index 000000000000..c09659342578 --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/isoch_in.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_ISOCH_IN_H__ +#define __MAUSB_HPAL_ISOCH_IN_H__ + +#include "hpal/hpal.h" + +#define MAUSB_ISOCH_IN_KVEC_NUM 3 + +int mausb_send_isoch_in_msg(struct mausb_device *dev, + struct mausb_event *event); +int mausb_receive_isoch_in_data(struct mausb_device *dev, + struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); + +#endif /* __MAUSB_HPAL_ISOCH_IN_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/isoch_out.h b/drivers/usb/mausb_host/include/hpal/isoch_out.h new file mode 100644 index 000000000000..5fffb7798b7c --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/isoch_out.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_ISOCH_OUT_H__ +#define __MAUSB_HPAL_ISOCH_OUT_H__ + +#include "common/mausb_event.h" +#include "hpal/hpal.h" + +int mausb_send_isoch_out_msg(struct mausb_device *ma_dev, + struct mausb_event *mausb_event, + struct mausb_urb_ctx *urb_ctx); +int mausb_receive_isoch_out(struct mausb_event *event); + +#endif /* __MAUSB_HPAL_ISOCH_OUT_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/mausb_events.h b/drivers/usb/mausb_host/include/hpal/mausb_events.h new file mode 100644 index 000000000000..a2605cb69c52 --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/mausb_events.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_MAUSB_EVENTS_H__ +#define __MAUSB_HPAL_MAUSB_EVENTS_H__ + +#include "common/mausb_event.h" +#include "hpal/hpal.h" +#include "link/mausb_ip_link.h" + +#define MANAGEMENT_EVENT_TIMEOUT 3000 + +int mausb_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel); +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); +int mausb_usbdevhandle_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +void mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std * + std_desc, + struct usb_endpoint_descriptor * + usb_std_desc); +void mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss * + ss_desc, + struct usb_endpoint_descriptor * + usb_std_desc, + struct usb_ss_ep_comp_descriptor * + usb_ss_desc); +int mausb_ephandle_event_to_user(struct mausb_device *dev, u16 device_handle, + u16 descriptor_size, void *descriptor, + u16 *ep_handle); +int mausb_ephandle_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epactivate_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle); +int mausb_epactivate_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epinactivate_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 ep_handle); +int mausb_epinactivate_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epreset_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle, + u8 tsp_flag); +int mausb_epreset_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_epdelete_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 ep_handle); +int mausb_epdelete_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_modifyep0_event_to_user(struct mausb_device *dev, + u16 device_handle, u16 *ep_handle, + __le16 max_packet_size); +int mausb_modifyep0_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_setusbdevaddress_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 response_timeout); +int mausb_setusbdevaddress_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +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); +int mausb_updatedev_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_usbdevdisconnect_event_to_user(struct mausb_device *dev, + u16 dev_handle); +int mausb_ping_event_to_user(struct mausb_device *dev); +int mausb_usbdevreset_event_to_user(struct mausb_device *dev, + u16 device_handle); +int mausb_usbdevreset_event_from_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_canceltransfer_event_to_user(struct mausb_device *dev, + u16 device_handle, + u16 ep_handle, u64 urb); +int mausb_canceltransfer_event_from_user(struct mausb_device *dev, + struct mausb_event *event); + +void mausb_dev_reset_req_event(struct mausb_event *event); +void mausb_cleanup_send_data_msg_event(struct mausb_event *event); +void mausb_cleanup_received_data_msg_event(struct mausb_event *event); +void mausb_cleanup_delete_data_transfer_event(struct mausb_event *event); + +#endif /* __MAUSB_HPAL_MAUSB_EVENTS_H__ */ diff --git a/drivers/usb/mausb_host/include/hpal/network_callbacks.h b/drivers/usb/mausb_host/include/hpal/network_callbacks.h new file mode 100644 index 000000000000..a10b590d53aa --- /dev/null +++ b/drivers/usb/mausb_host/include/hpal/network_callbacks.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_HPAL_NETWORK_CALLBACKS_H__ +#define __MAUSB_HPAL_NETWORK_CALLBACKS_H__ + +#include + +#include "link/mausb_ip_link.h" + +/* generic callback by default */ +void mausb_ip_callback(void *ctx, enum mausb_channel channel, + enum mausb_link_action action, int status, void *data); + +#endif /* __MAUSB_HPAL_NETWORK_CALLBACKS_H__ */ diff --git a/drivers/usb/mausb_host/include/link/mausb_ip_link.h b/drivers/usb/mausb_host/include/link/mausb_ip_link.h new file mode 100644 index 000000000000..416f15b8ce9d --- /dev/null +++ b/drivers/usb/mausb_host/include/link/mausb_ip_link.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_LINK_MAUSB_IP_LINK_H__ +#define __MAUSB_LINK_MAUSB_IP_LINK_H__ + +#include +#include +#include +#include + +#define MAUSB_LINK_BUFF_SIZE 16777216 +#define MAUSB_LINK_TOS_LEVEL_EF 0xB8 + +enum mausb_link_action { + MAUSB_LINK_CONNECT = 0, + MAUSB_LINK_DISCONNECT = 1, + MAUSB_LINK_RECV = 2, + MAUSB_LINK_SEND = 3 +}; + +enum mausb_channel { + MAUSB_CTRL_CHANNEL = 0, + MAUSB_ISOCH_CHANNEL = 1, + MAUSB_BULK_CHANNEL = 2, + MAUSB_INTR_CHANNEL = 3, + MAUSB_MGMT_CHANNEL = 4 +}; + +struct mausb_kvec_data_wrapper { + struct kvec *kvec; + u32 kvec_num; + u32 length; +}; + +struct mausb_ip_recv_ctx { + u16 left; + u16 received; + char *buffer; + char common_hdr[12] __aligned(4); +}; + +struct mausb_ip_ctx { + struct socket *client_socket; + struct net *net_ns; + char ip_addr[INET6_ADDRSTRLEN]; + u16 port; + bool udp; + + /* Queues to schedule rx work */ + struct workqueue_struct *recv_workq; + struct workqueue_struct *connect_workq; + struct work_struct recv_work; + struct work_struct connect_work; + + struct mausb_ip_recv_ctx recv_ctx; /* recv buffer */ + + enum mausb_channel channel; + void *ctx; + /* callback should store task into hpal queue */ + void (*fn_callback)(void *ctx, enum mausb_channel channel, + enum mausb_link_action act, int status, void *data); +}; + +int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx, + struct net *net_ns, + char ip_addr[INET6_ADDRSTRLEN], + u16 port, + void *ctx, + void (*ctx_callback)(void *ctx, + enum mausb_channel channel, + enum mausb_link_action act, + int status, void *data), + enum mausb_channel channel); +int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx); +int mausb_ip_send(struct mausb_ip_ctx *ip_ctx, + struct mausb_kvec_data_wrapper *wrapper); + +void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx); +void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx); + +#endif /* __MAUSB_LINK_MAUSB_IP_LINK_H__ */ diff --git a/drivers/usb/mausb_host/include/utils/mausb_data_iterator.h b/drivers/usb/mausb_host/include/utils/mausb_data_iterator.h new file mode 100644 index 000000000000..354b47f42b7a --- /dev/null +++ b/drivers/usb/mausb_host/include/utils/mausb_data_iterator.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_UTILS_MAUSB_DATA_ITERATOR_H__ +#define __MAUSB_UTILS_MAUSB_DATA_ITERATOR_H__ + +#include +#include +#include + +struct mausb_data_iter { + u32 length; + + void *buffer; + u32 buffer_len; + u32 offset; + + struct scatterlist *sg; + struct sg_mapping_iter sg_iter; + bool sg_started; + unsigned int num_sgs; + unsigned int flags; +}; + +struct mausb_payload_chunk { + struct list_head list_entry; + struct kvec kvec; +}; + +int mausb_data_iterator_read(struct mausb_data_iter *iterator, + u32 byte_num, + struct list_head *data_chunks_list, + u32 *data_chunks_num); + +u32 mausb_data_iterator_length(struct mausb_data_iter *iterator); +u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer, + u32 length); + +void mausb_init_data_iterator(struct mausb_data_iter *iterator, + void *buffer, u32 buffer_len, + struct scatterlist *sg, unsigned int num_sgs, + bool direction); +void mausb_reset_data_iterator(struct mausb_data_iter *iterator); +void mausb_uninit_data_iterator(struct mausb_data_iter *iterator); +void mausb_data_iterator_seek(struct mausb_data_iter *iterator, u32 seek_delta); + +#endif /* __MAUSB_UTILS_MAUSB_DATA_ITERATOR_H__ */ diff --git a/drivers/usb/mausb_host/include/utils/mausb_logs.h b/drivers/usb/mausb_host/include/utils/mausb_logs.h new file mode 100644 index 000000000000..d510061d2c34 --- /dev/null +++ b/drivers/usb/mausb_host/include/utils/mausb_logs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_UTILS_MAUSB_LOGS_H__ +#define __MAUSB_UTILS_MAUSB_LOGS_H__ + +#ifdef MAUSB_WITH_LOGS +#include +#define mausb_pr_logs(level_str, level, log, ...)\ + pr_##level_str("MAUSB " #level " [%x:%x] [%s] " log "\n",\ + current->pid, current->tgid, __func__, ##__VA_ARGS__) +#else +#define mausb_pr_logs(...) +#endif /* MAUSB_WITH_LOGS */ + +#define mausb_pr_alert(...) mausb_pr_logs(alert, 1, ##__VA_ARGS__) + +#define mausb_pr_err(...) mausb_pr_logs(err, 2, ##__VA_ARGS__) + +#define mausb_pr_warn(...) mausb_pr_logs(warn, 3, ##__VA_ARGS__) + +#define mausb_pr_info(...) mausb_pr_logs(info, 4, ##__VA_ARGS__) + +#if defined(MAUSB_LOG_VERBOSE) + #define mausb_pr_debug(...) mausb_pr_logs(debug, 5, ##__VA_ARGS__) +#else + #define mausb_pr_debug(...) +#endif /* defined(MAUSB_LOG_VERBOSE) */ + +#endif /* __MAUSB_UTILS_MAUSB_LOGS_H__ */ diff --git a/drivers/usb/mausb_host/include/utils/mausb_mmap.h b/drivers/usb/mausb_host/include/utils/mausb_mmap.h new file mode 100644 index 000000000000..efa4389fbb0b --- /dev/null +++ b/drivers/usb/mausb_host/include/utils/mausb_mmap.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_UTILS_MAUSB_MMAP_H__ +#define __MAUSB_UTILS_MAUSB_MMAP_H__ + +#include "utils/mausb_ring_buffer.h" + +int mausb_create_dev(void); + +void mausb_cleanup_dev(int device_created); +void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer); +void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer); +void mausb_stop_ring_events(void); + +#endif /* __MAUSB_UTILS_MAUSB_MMAP_H__ */ diff --git a/drivers/usb/mausb_host/include/utils/mausb_ring_buffer.h b/drivers/usb/mausb_host/include/utils/mausb_ring_buffer.h new file mode 100644 index 000000000000..465bad87aa89 --- /dev/null +++ b/drivers/usb/mausb_host/include/utils/mausb_ring_buffer.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef ___MAUSB_UTILS_MAUSB_RING_BUFFER_H__ +#define ___MAUSB_UTILS_MAUSB_RING_BUFFER_H__ + +#include +#include + +#include "common/mausb_event.h" +#include "utils/mausb_logs.h" + +struct mausb_ring_buffer { + atomic_t mausb_ring_events; + atomic_t mausb_completed_user_events; + + struct mausb_event *to_user_buffer; + int head; + int tail; + spinlock_t lock; /* Protect ring buffer */ + u64 id; + + struct mausb_event *from_user_buffer; + int current_from_user; + + struct list_head list_entry; + bool buffer_full; +}; + +int mausb_ring_buffer_init(struct mausb_ring_buffer *ring); +int mausb_ring_buffer_put(struct mausb_ring_buffer *ring, + struct mausb_event *event); +int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count); +void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring); +void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring); +void mausb_cleanup_ring_buffer_event(struct mausb_event *event); +void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring, + uint8_t madev_addr); + +static inline unsigned int mausb_get_page_order(unsigned int num_of_elems, + unsigned int elem_size) +{ + unsigned int num_of_pages = DIV_ROUND_UP(num_of_elems * elem_size, + PAGE_SIZE); + unsigned int order = (unsigned int)ilog2(num_of_pages) + + (is_power_of_2(num_of_pages) ? 0 : 1); + return order; +} + +static inline +struct mausb_event *mausb_ring_current_from_user(struct mausb_ring_buffer *ring) +{ + return ring->from_user_buffer + ring->current_from_user; +} + +static inline void mausb_ring_next_from_user(struct mausb_ring_buffer *ring) +{ + ring->current_from_user = (ring->current_from_user + 1) & + (MAUSB_RING_BUFFER_SIZE - 1); +} + +#endif /* ___MAUSB_UTILS_MAUSB_RING_BUFFER_H__ */ diff --git a/drivers/usb/mausb_host/include/utils/mausb_version.h b/drivers/usb/mausb_host/include/utils/mausb_version.h new file mode 100644 index 000000000000..c2c45ecd8885 --- /dev/null +++ b/drivers/usb/mausb_host/include/utils/mausb_version.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#ifndef __MAUSB_UTILS_MAUSB_VERSION_H__ +#define __MAUSB_UTILS_MAUSB_VERSION_H__ + +#define MAUSB_STRINGIFY2(x) #x +#define MAUSB_STRINGIFY(x) MAUSB_STRINGIFY2(x) + +#define MAUSB_DRIVER_VERSION MAUSB_STRINGIFY(1.1.0.0.9f6c4861) + +#endif /* __MAUSB_UTILS_MAUSB_VERSION_H__ */ diff --git a/drivers/usb/mausb_host/src/hcd/hub.c b/drivers/usb/mausb_host/src/hcd/hub.c new file mode 100644 index 000000000000..7063480d566e --- /dev/null +++ b/drivers/usb/mausb_host/src/hcd/hub.c @@ -0,0 +1,1682 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hcd/hub.h" + +#include +#include + +#include "hcd/vhcd.h" +#include "hpal/hpal.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_logs.h" + +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_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 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 void mausb_print_urb(struct urb *request) +{ + mausb_pr_debug("URB: urb=%p, ep_handle=%#x, packet_num=%d, setup_dma=%lld, 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=%llu, 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, + +#ifdef ISOCH_CALLBACKS + .sec_event_ring_setup = mausb_sec_event_ring_setup, + .sec_event_ring_cleanup = mausb_sec_event_ring_cleanup, + .get_sec_event_ring_phys_addr = mausb_get_sec_event_ring_phys_addr, + .get_xfer_ring_phys_addr = mausb_get_xfer_ring_phys_addr, + .get_core_id = mausb_get_core_id +#endif /* ISOCH_CALLBACKS */ + +}; + +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) { + mausb_pr_info("Trying to get roothub port number"); + (*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) +{ + 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; +} + +static int mausb_validate_urb(struct urb *urb) +{ + if (!urb) { + mausb_pr_err("urb is NULL"); + return -EINVAL; + } + + if (!urb->ep->hcpriv) { + mausb_pr_err("urb->ep->hcpriv is NULL"); + return -EINVAL; + } + + if (!urb->ep->enabled) { + mausb_pr_err("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) { + mausb_pr_err("Hpal urb enqueue failed"); + return -EPROTO; + } + + endpoint_ctx = urb->ep->hcpriv; + ma_dev = endpoint_ctx->ma_dev; + + if (atomic_read(&ma_dev->unresponsive_client)) { + mausb_pr_err("Client is not responsive anymore - finish urb immediately"); + return -EHOSTDOWN; + } + + urb->hcpriv = hcd; + + mausb_pr_debug("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) { + mausb_pr_err("Hpal urb enqueue failed"); + return status; + } + + atomic_inc(&urb->use_count); + + mausb_print_urb(urb); + + /* + * Masking URB_SHORT_NOT_OK flag as SCSI driver is adding it where it + * should not, so it is breaking the USB drive on the linux + */ + urb->transfer_flags &= ~URB_SHORT_NOT_OK; + + 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; + + mausb_pr_info("Urb=%p", urb); + + urb_ctx = mausb_unlink_and_delete_urb_from_tree(urb, status); + if (!urb_ctx) { + mausb_pr_warn("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); + return; + } +} + +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) { + mausb_pr_err("usb_create_hcd failed"); + 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) { + mausb_pr_err("usb_create_shared_hcd failed"); + 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_info("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_info("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_info("USB_PORT_FEAT_BH_PORT_RESET"); + /* fall through */ + case USB_PORT_FEAT_RESET: + mausb_pr_info("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_info("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; + unsigned long flags; + struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv; + struct mausb_dev *mdev = NULL; + 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) { + mausb_pr_info("port_number out of range, port_number=%x", + port_number); + return; + } + + mdev = &hub->ma_devs[port_number]; + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("MAUSB device not found on port_number=%d", + port_number); + return; + } + + 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 (atomic_read(&ma_dev->unresponsive_client)) { + mausb_pr_err("Client is not responsive anymore - free usbdevice immediately"); + dev->ep0.hcpriv = NULL; + kfree(ep_ctx); + goto free_dev; + } + + if (ep_ctx) { + status = mausb_epinactivate_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d", + ep_ctx->ep_handle, dev_handle, status); + + status = mausb_epdelete_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + if (status < 0) + mausb_pr_warn("ep_handle_del request failed for ep0: ep_handle=%#x, dev_handle=%#x", + ep_ctx->ep_handle, dev_handle); + dev->ep0.hcpriv = NULL; + kfree(ep_ctx); + + } else { + mausb_pr_warn("ep_ctx is NULL: dev_handle=%#x", dev_handle); + } + + if (dev_handle != DEV_HANDLE_NOT_ASSIGNED) { + status = mausb_usbdevdisconnect_event_to_user(ma_dev, + dev_handle); + if (status < 0) + mausb_pr_warn("usb_dev_disconnect req failed for dev_handle=%#x", + dev_handle); + } + +free_dev: + if (atomic_sub_and_test(1, &ma_dev->num_of_usb_devices)) { + mausb_pr_info("All usb devices destroyed - proceed with disconnecting"); + queue_work(ma_dev->workq, &ma_dev->socket_disconnect_work); + } + + 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); + + mausb_pr_info("kref_put"); + 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); + + mausb_pr_info("dev_handle=%#x, status=%d", usb_dev_ctx->dev_handle, + status); + 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, u16 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)) { + mausb_pr_warn("device_ctx already exists"); + kfree(usb_device_ctx); + *status = -EEXIST; + return NULL; + } + + kref_get(&ma_dev->refcount); + mausb_pr_info("New USB device added device=%p", + usb_device_ctx->dev_addr); + atomic_inc(&ma_dev->num_of_usb_devices); + + return 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; + unsigned long flags; + 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) { + mausb_pr_info("port_number out of range, port_number=%x", + port_number); + return -EINVAL; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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; + } + + 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; + } + + 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) { + mausb_pr_err("endpoint_ctx is NULL: dev_handle=%#x", + usb_device_ctx->dev_handle); + return -EINVAL; + } + + /* + * Fix to support usb 2.0 logitech camera. If endoint 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); + + mausb_pr_info("modify ep0 response, ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + 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; + unsigned long flags = 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 0; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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) { + 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_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) { + mausb_pr_err("ep_handle_request failed dev_handle=%#x", + usb_dev_ctx->dev_handle); + endpoint->hcpriv = NULL; + kfree(endpoint_ctx); + return status; + } + + mausb_pr_info("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; + unsigned long flags = 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; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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) { + 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; + } + + mausb_pr_info("Start dropping ep_handle=%#x, dev_handle=%#x", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle); + + if (atomic_read(&ma_dev->unresponsive_client)) { + mausb_pr_err("Client is not responsive anymore - drop endpoint immediately"); + endpoint->hcpriv = NULL; + kfree(endpoint_ctx); + return -ESHUTDOWN; + } + + status = mausb_epinactivate_event_to_user(ma_dev, + usb_dev_ctx->dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + while (true) { + status = mausb_epdelete_event_to_user(ma_dev, + usb_dev_ctx->dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("ep_handle_delete_request, ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + if (status == -EBUSY) { + if (++retries < MAUSB_BUSY_RETRIES_COUNT) + usleep_range(10000, 10001); + else + return -EBUSY; + } else { + break; + } + } + + mausb_pr_info("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) { + mausb_pr_info("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); + mausb_pr_info("start... mausb_devspeed=%d, route=%#x, port_number=%d", + dev_speed, dev->route, port_number); + + if (dev_speed == -EINVAL) { + mausb_pr_err("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); + + mausb_pr_info("mausb_usbdevhandle_event status=%d", status); + + if (status < 0) + return status; + + mausb_pr_info("Get ref 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); + + mausb_pr_info("mausb_ephandle_event ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, endpoint_ctx->dev_handle, + status); + + if (status < 0) { + dev->ep0.hcpriv = NULL; + kfree(endpoint_ctx); + return status; + } + + 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_device *ma_dev; + struct mausb_usb_device_ctx *usb_device_ctx; + unsigned long flags; + + 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; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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; + } + + mausb_pr_info("Device assigned and addressed usb_device_ctx=%p", + usb_device_ctx); + + if (usb_device_ctx->dev_handle == DEV_HANDLE_NOT_ASSIGNED) + return mausb_device_assign_dev_handle(hcd, dev, hub, ma_dev, + usb_device_ctx); + + /* + * Fix for usb 2.0 logitech camera + */ + if (!usb_device_ctx->addressed) + return mausb_device_assign_address(ma_dev, usb_device_ctx); + + mausb_pr_info("Device assigned and addressed dev_handle=%#x", + 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; + unsigned long flags = 0; + + 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; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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) { + mausb_pr_warn("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); + + mausb_pr_info("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; + unsigned long flags; + 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) { + mausb_pr_info("port_number out of range, port_number=%x", + port_number); + return 0; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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) { + 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; + + 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); + + mausb_pr_info("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) +{ + 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; + unsigned long flags; + 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) { + 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; + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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); + + mausb_pr_info("ep_reset status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + 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); + } + + status = mausb_epactivate_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_err("ep_activate failed, status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + return; + } + + if (tsp) + return; + + status = mausb_epinactivate_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_info("ep_inactivate status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + + status = mausb_epdelete_event_to_user(ma_dev, dev_handle, + ep_ctx->ep_handle); + + mausb_pr_info("ep_handle_delete status=%d, ep_handle=%#x, dev_handle=%#x", + status, ep_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + + 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, dev_handle, + sizeof(descriptor_ss), + &descriptor_ss, + &ep_ctx->ep_handle); + } else { + mausb_init_standard_ep_descriptor(&descriptor, &endpoint->desc); + status = mausb_ephandle_event_to_user(ma_dev, dev_handle, + sizeof(descriptor), + &descriptor, + &ep_ctx->ep_handle); + } + + 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; + unsigned long flags; + 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) { + mausb_pr_info("port_number out of range, port_number=%x", + port_number); + return -EINVAL; + } + + spin_lock_irqsave(&mhcd->lock, flags); + ma_dev = hub->ma_devs[port_number].ma_dev; + spin_unlock_irqrestore(&mhcd->lock, flags); + + if (!ma_dev) { + mausb_pr_err("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); + + mausb_pr_info("usb_dev_reset dev_handle=%#x, status=%d", + dev_handle, status); + + if (status == 0) + usb_device_ctx->addressed = false; + + return status; +} + +#ifdef ISOCH_CALLBACKS +int mausb_sec_event_ring_setup(struct usb_hcd *hcd, unsigned int intr_num) +{ + mausb_pr_debug(""); + return 0; +} + +int mausb_sec_event_ring_cleanup(struct usb_hcd *hcd, unsigned int intr_num) +{ + mausb_pr_debug(""); + return 0; +} + +phys_addr_t mausb_get_sec_event_ring_phys_addr(struct usb_hcd *hcd, + unsigned int intr_num, + dma_addr_t *dma) +{ + mausb_pr_debug(""); + return 0; +} + +phys_addr_t mausb_get_xfer_ring_phys_addr(struct usb_hcd *hcd, + struct usb_device *udev, + struct usb_host_endpoint *ep, + dma_addr_t *dma) +{ + mausb_pr_debug(""); + return 0; +} + +int mausb_get_core_id(struct usb_hcd *hcd) +{ + mausb_pr_debug(""); + return 0; +} +#endif /* ISOCH_CALLBACKS */ + +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/src/hcd/module_init.c b/drivers/usb/mausb_host/src/hcd/module_init.c new file mode 100644 index 000000000000..cae02eaf8e3b --- /dev/null +++ b/drivers/usb/mausb_host/src/hcd/module_init.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/mausb_address.h" +#include "hcd/vhcd.h" +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_mmap.h" +#include "utils/mausb_version.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("DisplayLink (UK) Ltd."); +MODULE_VERSION(MAUSB_DRIVER_VERSION); + +static struct mausb_device_address device_address; +static int mausb_device_disconnect_param; +static u16 madev_addr; +static u8 mausb_client_connect_param; +static u8 mausb_client_disconnect_param; + +static int mausb_client_connect(const char *value, + const struct kernel_param *kp) +{ + unsigned long flags = 0; + + mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION); + + spin_lock_irqsave(&mss.lock, flags); + if (mss.client_connected) { + mausb_pr_err("MA-USB client is already connected"); + spin_unlock_irqrestore(&mss.lock, flags); + return -EEXIST; + } + /* Save heartbeat client information */ + mss.client_connected = true; + mss.missed_heartbeats = 0; + reinit_completion(&mss.client_stopped); + spin_unlock_irqrestore(&mss.lock, flags); + /* Start hearbeat timer */ + mod_timer(&mss.heartbeat_timer, + jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS)); + + return 0; +} + +static int mausb_client_disconnect(const char *value, + const struct kernel_param *kp) +{ + unsigned long flags = 0; + struct mausb_device *dev = NULL; + + mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION); + + spin_lock_irqsave(&mss.lock, flags); + if (!mss.client_connected) { + mausb_pr_err("MA-USB client is not connected"); + spin_unlock_irqrestore(&mss.lock, flags); + return -ENODEV; + } + + spin_unlock_irqrestore(&mss.lock, flags); + + /* Stop heartbeat timer */ + del_timer_sync(&mss.heartbeat_timer); + + /* Clear heartbeat client information */ + spin_lock_irqsave(&mss.lock, flags); + mss.client_connected = false; + mss.missed_heartbeats = 0; + list_for_each_entry(dev, &mss.madev_list, list_entry) { + mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x", + dev->madev_addr); + queue_work(dev->workq, &dev->heartbeat_work); + } + complete(&mss.client_stopped); + spin_unlock_irqrestore(&mss.lock, flags); + + return 0; +} + +static int mausb_device_connect(const char *value, + const struct kernel_param *kp) +{ + int status = 0; + + mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION); + + if (strlen(value) <= INET_ADDRSTRLEN) { + strcpy(device_address.ip.address.ip4, value); + /* Add list of already connected devices */ + } else if (strlen(value) <= INET6_ADDRSTRLEN) { + /* Logic for ip6 */ + } else { + mausb_pr_err("Invalid IP format"); + return 0; + } + status = mausb_initiate_dev_connection(device_address, madev_addr); + memset(&device_address, 0, sizeof(device_address)); + + return status; +} + +static int mausb_device_disconnect(const char *value, + const struct kernel_param *kp) +{ + u8 dev_address = 0; + int status = 0; + unsigned long flags = 0; + struct mausb_device *dev = NULL; + + mausb_pr_info("Version=%s", MAUSB_DRIVER_VERSION); + + status = kstrtou8(value, 0, &dev_address); + if (status < 0) + return -EINVAL; + + spin_lock_irqsave(&mss.lock, flags); + + dev = mausb_get_dev_from_addr_unsafe(dev_address); + if (dev) + queue_work(dev->workq, &dev->hcd_disconnect_work); + + spin_unlock_irqrestore(&mss.lock, flags); + + return 0; +} + +static const struct kernel_param_ops mausb_device_connect_ops = { + .set = mausb_device_connect +}; + +static const struct kernel_param_ops mausb_device_disconnect_ops = { + .set = mausb_device_disconnect +}; + +static const struct kernel_param_ops mausb_client_connect_ops = { + .set = mausb_client_connect +}; + +static const struct kernel_param_ops mausb_client_disconnect_ops = { + .set = mausb_client_disconnect +}; + +module_param_named(mgmt, device_address.ip.port.management, ushort, 0664); +MODULE_PARM_DESC(mgmt, "MA-USB management port"); +module_param_named(ctrl, device_address.ip.port.control, ushort, 0664); +MODULE_PARM_DESC(ctrl, "MA-USB control port"); +module_param_named(bulk, device_address.ip.port.bulk, ushort, 0664); +MODULE_PARM_DESC(bulk, "MA-USB bulk port"); +module_param_named(isoch, device_address.ip.port.isochronous, ushort, 0664); +MODULE_PARM_DESC(isoch, "MA-USB isochronous port"); +module_param_named(madev_addr, madev_addr, ushort, 0664); +MODULE_PARM_DESC(madev_addr, "MA-USB device address"); + +module_param_cb(client_connect, &mausb_client_connect_ops, + &mausb_client_connect_param, 0664); +module_param_cb(client_disconnect, &mausb_client_disconnect_ops, + &mausb_client_disconnect_param, 0664); +module_param_cb(ip, &mausb_device_connect_ops, + device_address.ip.address.ip4, 0664); +module_param_cb(disconnect, &mausb_device_disconnect_ops, + &mausb_device_disconnect_param, 0664); + +static int host_mausb_init(void) +{ + int status; + + mausb_pr_info("Module load. Version=%s", MAUSB_DRIVER_VERSION); + status = mausb_init_hcd(); + if (status < 0) + goto cleanup; + + status = mausb_register_power_state_listener(); + if (status < 0) + goto cleanup_hcd; + + status = mausb_create_dev(); + if (status < 0) + goto unregister_power_state_listener; + + mausb_initialize_mss(); + + return 0; + +unregister_power_state_listener: + mausb_unregister_power_state_listener(); +cleanup_hcd: + mausb_deinit_hcd(); +cleanup: + mausb_pr_alert("Failed to load MAUSB module!"); + return status; +} + +static void host_mausb_exit(void) +{ + mausb_pr_info("Module unloading started..."); + mausb_unregister_power_state_listener(); + mausb_deinitialize_mss(); + mausb_deinit_hcd(); + mausb_cleanup_dev(1); + mausb_pr_info("Module unloaded. Version=%s", MAUSB_DRIVER_VERSION); +} + +module_init(host_mausb_init); +module_exit(host_mausb_exit); diff --git a/drivers/usb/mausb_host/src/hcd/vhcd.c b/drivers/usb/mausb_host/src/hcd/vhcd.c new file mode 100644 index 000000000000..4eb8b4a09977 --- /dev/null +++ b/drivers/usb/mausb_host/src/hcd/vhcd.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hcd/vhcd.h" + +#include +#include +#include + +#include "hcd/hub.h" +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" + +static int mausb_open(struct inode *inode, struct file *file); +static int mausb_release(struct inode *inode, struct file *file); +static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length, + loff_t *offset); +static ssize_t mausb_write(struct file *file, const char __user *buffer, + size_t length, loff_t *offset); +static long mausb_ioctl(struct file *file, unsigned int ioctl_func, + unsigned long ioctl_buffer); +static int mausb_bus_probe(struct device *dev); +static int mausb_bus_remove(struct device *dev); +static int mausb_bus_match(struct device *dev, struct device_driver *drv); + +static const struct file_operations mausb_fops = { + .open = mausb_open, + .release = mausb_release, + .read = mausb_read, + .write = mausb_write, + .unlocked_ioctl = mausb_ioctl +}; + +static unsigned int major; +static unsigned int minor = 1; +static dev_t devt; +static struct device *device; + +struct mausb_hcd *mhcd; +static struct class *mausb_class; +static struct bus_type mausb_bus_type = { + .name = DEVICE_NAME, + .match = mausb_bus_match, + .probe = mausb_bus_probe, + .remove = mausb_bus_remove, +}; + +static struct device_driver mausb_driver = { + .name = DEVICE_NAME, + .bus = &mausb_bus_type, + .owner = THIS_MODULE, +}; + +static void mausb_remove(void) +{ + struct usb_hcd *hcd, *shared_hcd; + + hcd = mhcd->hcd_hs_ctx->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; + } + + usb_remove_hcd(hcd); + usb_put_hcd(hcd); + mhcd->hcd_hs_ctx = NULL; +} + +static int mausb_bus_probe(struct device *dev) +{ + return mausb_probe(dev); +} + +static int mausb_bus_remove(struct device *dev) +{ + return 0; +} + +static int mausb_bus_match(struct device *dev, struct device_driver *drv) +{ + if (strncmp(dev->bus->name, drv->name, strlen(drv->name))) + return 0; + else + return 1; +} + +static int mausb_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int mausb_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length, + loff_t *offset) +{ + return 0; +} + +static ssize_t mausb_write(struct file *file, const char __user *buffer, + size_t length, loff_t *offset) +{ + return 0; +} + +static long mausb_ioctl(struct file *file, unsigned int ioctl_func, + unsigned long ioctl_buffer) +{ + return 0; +} + +int mausb_init_hcd(void) +{ + int retval; + + retval = register_chrdev(0, DEVICE_NAME, &mausb_fops); + if (retval < 0) { + mausb_pr_err("Register_chrdev failed"); + return retval; + } + + major = (unsigned int)retval; + retval = bus_register(&mausb_bus_type); + if (retval) { + mausb_pr_err("Bus_register failed %d", retval); + goto bus_register_error; + } + + mausb_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(mausb_class)) { + mausb_pr_err("Class_create failed %ld", PTR_ERR(mausb_class)); + goto class_error; + } + + retval = driver_register(&mausb_driver); + if (retval) { + mausb_pr_err("Driver_register failed"); + goto driver_register_error; + } + + mhcd = kzalloc(sizeof(*mhcd), GFP_ATOMIC); + if (!mhcd) { + retval = -ENOMEM; + goto mausb_hcd_alloc_failed; + } + + devt = MKDEV(major, minor); + device = device_create(mausb_class, NULL, devt, mhcd, DEVICE_NAME); + + if (IS_ERR(device)) { + mausb_pr_err("Device_create failed %ld", PTR_ERR(device)); + goto device_create_error; + } + + 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; +mausb_hcd_alloc_failed: + driver_unregister(&mausb_driver); +driver_register_error: + class_destroy(mausb_class); +class_error: + bus_unregister(&mausb_bus_type); +bus_register_error: + unregister_chrdev(major, DEVICE_NAME); + + return retval; +} + +void mausb_deinit_hcd(void) +{ + mausb_pr_info("Start"); + + if (mhcd) { + mausb_remove(); + device_destroy(mausb_class, devt); + driver_unregister(&mausb_driver); + class_destroy(mausb_class); + bus_unregister(&mausb_bus_type); + unregister_chrdev(major, DEVICE_NAME); + } + + mausb_pr_info("Finish"); +} + +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 = 0; + struct mausb_device *dev = ma_dev; + u16 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 u16 port_number, + const enum mausb_device_type device_type, + const enum mausb_device_speed device_speed) +{ + struct usb_hcd *hcd; + 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); + + 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); +} diff --git a/drivers/usb/mausb_host/src/hpal/data_common.c b/drivers/usb/mausb_host/src/hpal/data_common.c new file mode 100644 index 000000000000..c3fef0e2c9b8 --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/data_common.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hpal/data_common.h" + +#include "hpal/data_in.h" +#include "hpal/data_out.h" +#include "hpal/isoch_in.h" +#include "hpal/isoch_out.h" +#include "utils/mausb_logs.h" + +static inline +struct mausb_ip_ctx *mausb_get_data_channel(struct mausb_device *ma_dev, + enum mausb_channel channel) +{ + if (channel >= MAUSB_CHANNEL_MAP_LENGTH) + return NULL; + + return ma_dev->channel_map[channel]; +} + +int mausb_send_data(struct mausb_device *dev, enum mausb_channel channel_num, + struct mausb_kvec_data_wrapper *data) +{ + struct mausb_ip_ctx *channel = mausb_get_data_channel(dev, channel_num); + int status = 0; + + if (!channel) + return -ECHRNG; + + status = mausb_ip_send(channel, data); + + if (status < 0) { + mausb_pr_err("Send failed. Disconnecting... status=%d", status); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + return status; +} + +int mausb_send_transfer_ack(struct mausb_device *dev, struct mausb_event *event) +{ + struct ma_usb_hdr_common *ack_hdr; + struct kvec kvec; + struct mausb_kvec_data_wrapper data_to_send; + enum mausb_channel channel; + + ack_hdr = (struct ma_usb_hdr_common *)(&event->data.hdr_ack); + + data_to_send.kvec = &kvec; + data_to_send.kvec->iov_base = ack_hdr; + data_to_send.kvec->iov_len = ack_hdr->length; + data_to_send.kvec_num = 1; + data_to_send.length = ack_hdr->length; + + channel = mausb_transfer_type_to_channel(event->data.transfer_type); + return mausb_send_data(dev, channel, &data_to_send); +} + +int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event) +{ + struct mausb_urb_ctx *urb_ctx; + int status = 0; + + if (event->status != 0) { + mausb_pr_err("Event %d failed with status %d", + event->type, event->status); + mausb_complete_urb(event); + return event->status; + } + + urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb); + + if (!urb_ctx) { + /* Transfer will be deleted from dequeue task */ + mausb_pr_warn("Urb is already cancelled for event=%d", + event->type); + return status; + } + + if (mausb_isoch_data_event(event)) { + if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN) + status = mausb_send_isoch_in_msg(dev, event); + else + status = mausb_send_isoch_out_msg(dev, event, urb_ctx); + } else { + if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN) + status = mausb_send_in_data_msg(dev, event); + else + status = mausb_send_out_data_msg(dev, event, urb_ctx); + } + + return status; +} + +int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event) +{ + int status = 0; + struct mausb_urb_ctx *urb_ctx; + + mausb_pr_debug("Direction=%d", event->data.direction); + + if (!mausb_isoch_data_event(event)) { + status = mausb_send_transfer_ack(dev, event); + if (status < 0) { + mausb_pr_warn("Sending acknowledgment failed"); + goto cleanup; + } + } + + urb_ctx = mausb_find_urb_in_tree((struct urb *)event->data.urb); + + if (!urb_ctx) { + /* Transfer will be deleted from dequeue task */ + mausb_pr_warn("Urb is already cancelled"); + goto cleanup; + } + + if (mausb_isoch_data_event(event)) { + if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN) + status = mausb_receive_isoch_in_data(dev, event, + urb_ctx); + else + status = mausb_receive_isoch_out(event); + } else { + if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN) + mausb_receive_in_data(event, urb_ctx); + else + mausb_receive_out_data(event, urb_ctx); + } + +cleanup: + mausb_release_event_resources(event); + return status; +} diff --git a/drivers/usb/mausb_host/src/hpal/data_in.c b/drivers/usb/mausb_host/src/hpal/data_in.c new file mode 100644 index 000000000000..4e3945b3145f --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/data_in.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hpal/data_in.h" + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_logs.h" + +int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event) +{ + struct mausb_kvec_data_wrapper data_to_send; + struct kvec kvec[2]; + struct urb *urb = (struct urb *)(event->data.urb); + bool setup_packet = (usb_endpoint_xfer_control(&urb->ep->desc) && + urb->setup_packet); + u32 kvec_num = setup_packet ? 2 : 1; + enum mausb_channel channel; + + data_to_send.kvec_num = kvec_num; + data_to_send.length = MAUSB_TRANSFER_HDR_SIZE + + (setup_packet ? MAUSB_CONTROL_SETUP_SIZE : 0); + + /* Prepare transfer header kvec */ + kvec[0].iov_base = event->data.hdr; + kvec[0].iov_len = MAUSB_TRANSFER_HDR_SIZE; + + /* Prepare setup packet kvec */ + if (setup_packet) { + kvec[1].iov_base = urb->setup_packet; + kvec[1].iov_len = MAUSB_CONTROL_SETUP_SIZE; + } + data_to_send.kvec = kvec; + + channel = mausb_transfer_type_to_channel(event->data.transfer_type); + return mausb_send_data(dev, channel, &data_to_send); +} + +void mausb_receive_in_data(struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx) +{ + struct urb *urb = urb_ctx->urb; + struct mausb_data_iter *iterator = &urb_ctx->iterator; + struct ma_usb_hdr_common *common_hdr = + (struct ma_usb_hdr_common *)event->data.recv_buf; + void *buffer; + u32 payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE; + u32 data_written = 0; + + buffer = shift_ptr(common_hdr, MAUSB_TRANSFER_HDR_SIZE); + data_written = mausb_data_iterator_write(iterator, buffer, + payload_size); + + mausb_pr_debug("data_written=%d, payload_size=%d", data_written, + payload_size); + event->data.rem_transfer_size -= data_written; + + if (event->data.transfer_eot) { + mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d", + event->data.transfer_size, + event->data.rem_transfer_size, event->status); + mausb_complete_request(urb, event->data.transfer_size - + event->data.rem_transfer_size, + event->status); + } +} diff --git a/drivers/usb/mausb_host/src/hpal/data_out.c b/drivers/usb/mausb_host/src/hpal/data_out.c new file mode 100644 index 000000000000..d92f6b0482d0 --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/data_out.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include + +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_logs.h" + +static int mausb_add_data_chunk(void *buffer, u32 buffer_size, + struct list_head *chunks_list) +{ + struct mausb_payload_chunk *data_chunk; + + data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL); + if (!data_chunk) + return -ENOMEM; + + /* Initialize data chunk for MAUSB header and add it to chunks list */ + INIT_LIST_HEAD(&data_chunk->list_entry); + + data_chunk->kvec.iov_base = buffer; + data_chunk->kvec.iov_len = buffer_size; + list_add_tail(&data_chunk->list_entry, chunks_list); + return 0; +} + +static int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data, + struct list_head *chunks_list, + u32 num_of_data_chunks) +{ + struct mausb_payload_chunk *data_chunk = NULL, + *tmp = NULL; + u32 current_kvec = 0; + + data->length = 0; + data->kvec = + kcalloc(num_of_data_chunks, sizeof(struct kvec), GFP_KERNEL); + if (!data->kvec) + return -ENOMEM; + + list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) { + data->kvec[current_kvec].iov_base = + data_chunk->kvec.iov_base; + data->kvec[current_kvec].iov_len = + data_chunk->kvec.iov_len; + ++data->kvec_num; + data->length += data_chunk->kvec.iov_len; + ++current_kvec; + } + return 0; +} + +static int mausb_init_header_data_chunk(struct ma_usb_hdr_common *common_hdr, + struct list_head *chunks_list, + u32 *num_of_data_chunks) +{ + int status = mausb_add_data_chunk(common_hdr, MAUSB_TRANSFER_HDR_SIZE, + chunks_list); + if (!status) + ++(*num_of_data_chunks); + + return status; +} + +static int mausb_init_control_data_chunk(struct mausb_event *event, + struct list_head *chunks_list, + u32 *num_of_data_chunks) +{ + int status; + void *buffer = ((struct urb *)event->data.urb)->setup_packet; + + if (!event->data.first_control_packet) + return 0; + + status = mausb_add_data_chunk(buffer, MAUSB_CONTROL_SETUP_SIZE, + chunks_list); + if (!status) + ++(*num_of_data_chunks); + + return status; +} + +static void mausb_cleanup_chunks_list(struct list_head *chunks_list) +{ + struct mausb_payload_chunk *data_chunk = NULL, *tmp = NULL; + + list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) { + list_del(&data_chunk->list_entry); + kfree(data_chunk); + } +} + +static int +mausb_prepare_transfer_packet(struct mausb_kvec_data_wrapper *wrapper, + struct mausb_event *event, + struct mausb_data_iter *iterator) +{ + u32 num_of_data_chunks = 0; + u32 num_of_payload_data_chunks = 0; + u32 payload_data_size = 0; + int status = 0; + struct list_head chunks_list; + struct list_head payload_data_chunks; + struct ma_usb_hdr_common *data_hdr = (struct ma_usb_hdr_common *) + event->data.hdr; + + INIT_LIST_HEAD(&chunks_list); + + /* Initialize data chunk for MAUSB header and add it to chunks list */ + if (mausb_init_header_data_chunk(data_hdr, &chunks_list, + &num_of_data_chunks) < 0) { + status = -ENOMEM; + goto cleanup_data_chunks; + } + + /* + * Initialize data chunk for MAUSB control setup packet and + * add it to chunks list + */ + if (mausb_init_control_data_chunk(event, &chunks_list, + &num_of_data_chunks) < 0) { + status = -ENOMEM; + goto cleanup_data_chunks; + } + + /* Get data chunks for data payload to send */ + INIT_LIST_HEAD(&payload_data_chunks); + payload_data_size = + ((struct ma_usb_hdr_common *)event->data.hdr)->length - + MAUSB_TRANSFER_HDR_SIZE - + (event->data.first_control_packet ? + MAUSB_CONTROL_SETUP_SIZE : 0); + + if (mausb_data_iterator_read(iterator, payload_data_size, + &payload_data_chunks, + &num_of_payload_data_chunks) < 0) { + status = -ENOMEM; + goto cleanup_data_chunks; + } + + list_splice_tail(&payload_data_chunks, &chunks_list); + num_of_data_chunks += num_of_payload_data_chunks; + + /* Map all data chunks to data wrapper */ + if (mausb_init_data_wrapper(wrapper, &chunks_list, + num_of_data_chunks) < 0) { + status = -ENOMEM; + goto cleanup_data_chunks; + } + +cleanup_data_chunks: /* Cleanup all allocated data chunks */ + mausb_cleanup_chunks_list(&chunks_list); + return status; +} + +int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx) +{ + int status; + struct mausb_kvec_data_wrapper data; + enum mausb_channel channel; + + status = mausb_prepare_transfer_packet(&data, event, + &urb_ctx->iterator); + + if (status < 0) { + mausb_pr_err("Failed to prepare transfer packet"); + return status; + } + + channel = mausb_transfer_type_to_channel(event->data.transfer_type); + status = mausb_send_data(dev, channel, &data); + + kfree(data.kvec); + + return status; +} + +void mausb_receive_out_data(struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx) +{ + struct urb *urb = urb_ctx->urb; + + mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d", + event->data.transfer_size, event->data.rem_transfer_size, + event->status); + + if (event->data.transfer_eot) { + mausb_complete_request(urb, urb->transfer_buffer_length - + event->data.rem_transfer_size, + event->status); + } +} diff --git a/drivers/usb/mausb_host/src/hpal/hpal.c b/drivers/usb/mausb_host/src/hpal/hpal.c new file mode 100644 index 000000000000..4a36c8b9b9ff --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/hpal.c @@ -0,0 +1,1306 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hpal/hpal.h" + +#include +#include +#include +#include +#include + +#include "common/mausb_event.h" +#include "hcd/hub.h" +#include "hcd/vhcd.h" +#include "hpal/mausb_events.h" +#include "hpal/data_common.h" +#include "hpal/network_callbacks.h" +#include "link/mausb_ip_link.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_mmap.h" +#include "utils/mausb_ring_buffer.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_version.h" + +#define MAUSB_DELETE_MADEV_TIMEOUT_MS 3000 + +struct mss mss; + +static int mausb_start_connection_timer(struct mausb_device *dev); +static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action, + void *data); +static void mausb_signal_empty_mss(void); +static void mausb_remove_madev_from_list(u8 madev_addr); +static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work); +static int mausb_start_heartbeat_timer(void); + +static inline struct mausb_urb_ctx *__mausb_find_urb_in_tree(struct urb *urb) +{ + struct rb_node *node = mhcd->mausb_urbs.rb_node; + + while (node) { + struct mausb_urb_ctx *urb_ctx = + rb_entry(node, struct mausb_urb_ctx, rb_node); + + if (urb < urb_ctx->urb) + node = urb_ctx->rb_node.rb_left; + else if (urb > urb_ctx->urb) + node = urb_ctx->rb_node.rb_right; + else + return urb_ctx; + } + return NULL; +} + +struct mausb_urb_ctx *mausb_find_urb_in_tree(struct urb *urb) +{ + unsigned long flags = 0; + struct mausb_urb_ctx *urb_ctx; + + spin_lock_irqsave(&mhcd->lock, flags); + urb_ctx = __mausb_find_urb_in_tree(urb); + spin_unlock_irqrestore(&mhcd->lock, flags); + + return urb_ctx; +} + +static int mausb_insert_urb_ctx_in_tree(struct mausb_urb_ctx *urb_ctx) +{ + struct rb_node **new_node = &mhcd->mausb_urbs.rb_node; + struct rb_node *parent = NULL; + struct mausb_urb_ctx *current_urb = NULL; + + while (*new_node) { + parent = *new_node; + current_urb = rb_entry(*new_node, struct mausb_urb_ctx, + rb_node); + + if (urb_ctx->urb < current_urb->urb) + new_node = &((*new_node)->rb_left); + else if (urb_ctx->urb > current_urb->urb) + new_node = &((*new_node)->rb_right); + else + return -EEXIST; + } + rb_link_node(&urb_ctx->rb_node, parent, new_node); + rb_insert_color(&urb_ctx->rb_node, &mhcd->mausb_urbs); + return 0; +} + +static void mausb_delete_urb_ctx_from_tree(struct mausb_urb_ctx *urb_ctx) +{ + rb_erase(&urb_ctx->rb_node, &mhcd->mausb_urbs); +} + +static struct mausb_urb_ctx *mausb_create_urb_ctx(struct urb *urb, int *status) +{ + struct mausb_urb_ctx *urb_ctx = NULL; + + if (!urb) { + mausb_pr_err("Urb is NULL"); + *status = -EINVAL; + return NULL; + } + + urb_ctx = kzalloc(sizeof(*urb_ctx), GFP_ATOMIC); + if (!urb_ctx) { + *status = -ENOMEM; + return NULL; + } + + urb_ctx->urb = urb; + INIT_WORK(&urb_ctx->work, mausb_execute_urb_dequeue); + + return urb_ctx; +} + +int mausb_insert_urb_in_tree(struct urb *urb, bool link_urb_to_ep) +{ + unsigned long flags; + int status = 0; + + struct mausb_urb_ctx *urb_ctx = mausb_create_urb_ctx(urb, &status); + + if (!urb_ctx) + return status; + + spin_lock_irqsave(&mhcd->lock, flags); + + if (link_urb_to_ep) { + status = usb_hcd_link_urb_to_ep(urb->hcpriv, urb); + if (status) { + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("Error %d while linking urb to hcd_endpoint", + status); + kfree(urb_ctx); + return status; + } + } + + if (mausb_insert_urb_ctx_in_tree(urb_ctx)) { + kfree(urb_ctx); + if (link_urb_to_ep) + usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb); + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("Urb_ctx insertion failed"); + return -EEXIST; + } + + mausb_init_data_iterator(&urb_ctx->iterator, urb->transfer_buffer, + urb->transfer_buffer_length, urb->sg, + (unsigned int)urb->num_sgs, + usb_urb_dir_in(urb)); + + spin_unlock_irqrestore(&mhcd->lock, flags); + + return 0; +} + +static bool mausb_return_urb_ctx_to_tree(struct mausb_urb_ctx *urb_ctx, + bool link_urb_to_ep) +{ + unsigned long flags; + int status; + + if (!urb_ctx) + return false; + + spin_lock_irqsave(&mhcd->lock, flags); + if (link_urb_to_ep) { + status = usb_hcd_link_urb_to_ep(urb_ctx->urb->hcpriv, + urb_ctx->urb); + if (status) { + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("Error %d while linking urb to hcd_endpoint", + status); + return false; + } + } + + if (mausb_insert_urb_ctx_in_tree(urb_ctx)) { + if (link_urb_to_ep) + usb_hcd_unlink_urb_from_ep(urb_ctx->urb->hcpriv, + urb_ctx->urb); + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("Urb_ctx insertion failed"); + return false; + } + + spin_unlock_irqrestore(&mhcd->lock, flags); + + return true; +} + +static void mausb_complete_urbs_from_tree(void) +{ + struct mausb_urb_ctx *urb_ctx = NULL; + struct urb *current_urb = NULL; + struct rb_node *current_node = NULL; + unsigned long flags; + int status = 0; + int ret; + + mausb_pr_debug("Completing all urbs from tree"); + + spin_lock_irqsave(&mhcd->lock, flags); + + while ((current_node = rb_first(&mhcd->mausb_urbs))) { + urb_ctx = rb_entry(current_node, struct mausb_urb_ctx, rb_node); + + current_urb = urb_ctx->urb; + mausb_delete_urb_ctx_from_tree(urb_ctx); + mausb_uninit_data_iterator(&urb_ctx->iterator); + kfree(urb_ctx); + + ret = usb_hcd_check_unlink_urb(current_urb->hcpriv, + current_urb, status); + if (ret == -EIDRM) + mausb_pr_warn("Urb=%p is already unlinked", + current_urb); + else + usb_hcd_unlink_urb_from_ep(current_urb->hcpriv, + current_urb); + + spin_unlock_irqrestore(&mhcd->lock, flags); + + /* Prepare urb for completion */ + mausb_pr_debug("Completing urb=%p", current_urb); + + current_urb->status = -EPROTO; + current_urb->actual_length = 0; + atomic_dec(¤t_urb->use_count); + usb_hcd_giveback_urb(current_urb->hcpriv, current_urb, + current_urb->status); + + spin_lock_irqsave(&mhcd->lock, flags); + } + + spin_unlock_irqrestore(&mhcd->lock, flags); + + mausb_pr_debug("Completed all urbs from tree"); +} + +/*After this function call only valid thing to do with urb is to give it back*/ +struct mausb_urb_ctx *mausb_unlink_and_delete_urb_from_tree(struct urb *urb, + int status) +{ + struct mausb_urb_ctx *urb_ctx = NULL; + unsigned long flags; + int ret; + + if (!urb) { + mausb_pr_warn("Urb is NULL"); + return NULL; + } + + spin_lock_irqsave(&mhcd->lock, flags); + + urb_ctx = __mausb_find_urb_in_tree(urb); + + if (!urb_ctx) { + mausb_pr_warn("Urb=%p not in tree", urb); + spin_unlock_irqrestore(&mhcd->lock, flags); + return NULL; + } + + ret = usb_hcd_check_unlink_urb(urb->hcpriv, urb, status); + + if (ret == -EIDRM) + mausb_pr_warn("Urb=%p is already unlinked", urb); + else + usb_hcd_unlink_urb_from_ep(urb->hcpriv, urb); + + mausb_delete_urb_ctx_from_tree(urb_ctx); + + spin_unlock_irqrestore(&mhcd->lock, flags); + + mausb_pr_debug("Urb=%p is removed from tree", urb); + + return urb_ctx; +} + +void mausb_release_event_resources(struct mausb_event *event) +{ + struct ma_usb_hdr_common *receive_buffer = (struct ma_usb_hdr_common *) + event->data.recv_buf; + + kfree(receive_buffer); +} + +static void mausb_iterator_reset(struct mausb_device *dev, + struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + struct mausb_urb_ctx *urb_ctx; + + urb_ctx = mausb_find_urb_in_tree(urb); + + if (urb_ctx) + mausb_reset_data_iterator(&urb_ctx->iterator); +} + +static void mausb_iterator_seek(struct mausb_device *dev, + struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + struct mausb_urb_ctx *urb_ctx; + + urb_ctx = mausb_find_urb_in_tree(urb); + + if (urb_ctx) + mausb_data_iterator_seek(&urb_ctx->iterator, + event->data.iterator_seek_delta); +} + +void mausb_complete_urb(struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + + mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d", + event->data.transfer_size, + event->data.rem_transfer_size, event->status); + mausb_complete_request(urb, + event->data.transfer_size - + event->data.rem_transfer_size, + event->status); +} + +static void mausb_delete_ma_dev(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_signal_event(dev, event, event->mgmt.delete_ma_dev.event_id); +} + +static void mausb_process_user_finished(struct mausb_device *dev, + struct mausb_event *event) +{ + complete(&dev->user_finished_event); +} + +static int mausb_send_mgmt_msg(struct mausb_device *dev, + struct mausb_event *event) +{ + struct mausb_kvec_data_wrapper wrapper; + struct kvec kvec; + struct ma_usb_hdr_common *hdr; + int status; + + hdr = (struct ma_usb_hdr_common *)event->mgmt.mgmt_hdr.hdr; + + mausb_pr_info("event=%d, type=%d", event->type, hdr->type); + + kvec.iov_base = hdr; + kvec.iov_len = hdr->length; + wrapper.kvec = &kvec; + wrapper.kvec_num = 1; + wrapper.length = hdr->length; + + status = mausb_ip_send(dev->mgmt_channel, &wrapper); + if (status < 0) { + mausb_pr_err("Send failed. Disconnecting... status=%d", status); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + return status; +} + +static int mausb_get_first_free_port_number(u16 *port_number) +{ + (*port_number) = 0; + while ((mhcd->connected_ports & (1 << *port_number)) != 0 && + *port_number < NUMBER_OF_PORTS) + ++(*port_number); + + if (*port_number == NUMBER_OF_PORTS) + return -EINVAL; + + mhcd->connected_ports |= (1 << *port_number); + + return 0; +} + +static inline void mausb_port_has_changed_event(struct mausb_device *dev, + struct mausb_event *event) +{ + int status; + u16 port_number; + unsigned long flags = 0; + + spin_lock_irqsave(&mhcd->lock, flags); + + status = mausb_get_first_free_port_number(&port_number); + if (status < 0) { + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("There is no free port, schedule delete ma_dev"); + queue_work(dev->workq, &dev->socket_disconnect_work); + return; + } + + spin_unlock_irqrestore(&mhcd->lock, flags); + + dev->dev_type = event->port_changed.dev_type; + dev->dev_speed = event->port_changed.dev_speed; + dev->lse = event->port_changed.lse; + dev->dev_connected = 1; + dev->port_number = port_number; + + mausb_port_has_changed(event->port_changed.dev_type, + event->port_changed.dev_speed, dev); + + if ((enum mausb_device_type)event->port_changed.dev_type == USB30HUB) + mausb_port_has_changed(USB20HUB, HIGH_SPEED, dev); +} + +static void mausb_complete_timeout_event(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_pr_debug("Event type=%d, event_id=%llu", event->type, + event->mgmt.mgmt_req_timedout.event_id); + mausb_signal_event(dev, event, event->mgmt.mgmt_req_timedout.event_id); +} + +static void mausb_process_event(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_pr_debug("Event type=%d", event->type); + + switch (event->type) { + case MAUSB_EVENT_TYPE_USB_DEV_HANDLE: + mausb_usbdevhandle_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE: + mausb_ephandle_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_ACTIVATE: + mausb_epactivate_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_INACTIVATE: + mausb_epinactivate_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_RESET: + mausb_epreset_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_EP_HANDLE_DELETE: + mausb_epdelete_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_MODIFY_EP0: + mausb_modifyep0_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_SET_USB_DEV_ADDRESS: + mausb_setusbdevaddress_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_UPDATE_DEV: + mausb_updatedev_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_USB_DEV_RESET: + mausb_usbdevreset_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_CANCEL_TRANSFER: + mausb_canceltransfer_event_from_user(dev, event); + break; + case MAUSB_EVENT_TYPE_PORT_CHANGED: + mausb_port_has_changed_event(dev, event); + break; + case MAUSB_EVENT_TYPE_PING: + mausb_send_mgmt_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_SEND_MGMT_MSG: + mausb_send_mgmt_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_SEND_DATA_MSG: + mausb_send_data_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG: + mausb_receive_data_msg(dev, event); + break; + case MAUSB_EVENT_TYPE_URB_COMPLETE: + mausb_complete_urb(event); + break; + case MAUSB_EVENT_TYPE_SEND_ACK: + mausb_send_transfer_ack(dev, event); + mausb_release_event_resources(event); + break; + case MAUSB_EVENT_TYPE_ITERATOR_RESET: + mausb_iterator_reset(dev, event); + break; + case MAUSB_EVENT_TYPE_ITERATOR_SEEK: + mausb_iterator_seek(dev, event); + break; + case MAUSB_EVENT_TYPE_DELETE_MA_DEV: + mausb_delete_ma_dev(dev, event); + break; + case MAUSB_EVENT_TYPE_USER_FINISHED: + mausb_process_user_finished(dev, event); + break; + case MAUSB_EVENT_TYPE_RELEASE_EVENT_RESOURCES: + mausb_release_event_resources(event); + break; + case MAUSB_EVENT_TYPE_NONE: + mausb_release_event_resources(event); + break; + case MAUSB_EVENT_TYPE_MGMT_REQUEST_TIMED_OUT: + mausb_complete_timeout_event(dev, event); + break; + default: + break; + } + + mausb_notify_completed_user_events(dev->ring_buffer); +} + +static void mausb_hpal_kernel_work(struct work_struct *work) +{ + struct mausb_device *dev = container_of(work, struct mausb_device, + work); + struct mausb_event *event; + int status; + u16 i; + u16 events; + u16 completed_events; + unsigned long flags; + struct mausb_ring_buffer *dev_mausb_ring = dev->ring_buffer; + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + events = dev->num_of_user_events; + completed_events = dev->num_of_completed_events; + dev->num_of_user_events = 0; + dev->num_of_completed_events = 0; + spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags); + + status = mausb_ring_buffer_move_tail(dev_mausb_ring, completed_events); + if (status < 0) { + mausb_pr_err("Dequeue failed, status=%d", status); + kref_put(&dev->refcount, mausb_release_ma_dev_async); + return; + } + + for (i = 0; i < events; ++i) { + event = mausb_ring_current_from_user(dev_mausb_ring); + mausb_ring_next_from_user(dev_mausb_ring); + mausb_process_event(dev, event); + } +} + +static void mausb_socket_disconnect_event(struct work_struct *work) +{ + struct mausb_device *dev = container_of(work, struct mausb_device, + socket_disconnect_work); + struct mausb_event event; + int status; + + mausb_pr_info("madev_addr=%d", dev->madev_addr); + + mausb_ip_disconnect(dev->ctrl_channel); + mausb_destroy_ip_ctx(dev->ctrl_channel); + dev->ctrl_channel = NULL; + + mausb_ip_disconnect(dev->bulk_channel); + mausb_destroy_ip_ctx(dev->bulk_channel); + dev->bulk_channel = NULL; + + mausb_ip_disconnect(dev->isoch_channel); + mausb_destroy_ip_ctx(dev->isoch_channel); + dev->isoch_channel = NULL; + + if (dev->mgmt_channel) { + memset(&event, 0, sizeof(event)); + event.type = MAUSB_EVENT_TYPE_NETWORK_DISCONNECTED; + event.data.device_id = dev->id; + + status = mausb_enqueue_event_to_user(dev, &event); + + mausb_pr_info("Sending notification to user that network is disconnected status=%d", + status); + + mausb_pr_info("Releasing MAUSB device ref"); + kref_put(&dev->refcount, mausb_release_ma_dev_async); + } + + mausb_ip_disconnect(dev->mgmt_channel); + mausb_destroy_ip_ctx(dev->mgmt_channel); + dev->mgmt_channel = NULL; + + memset(dev->channel_map, 0, sizeof(dev->channel_map)); +} + +static void mausb_disconnect_ma_dev(struct mausb_device *dev) +{ + mausb_pr_info("Disconnecting MAUSB device madev_addr=%d", + dev->madev_addr); + + if (!dev->dev_connected) { + mausb_pr_warn("MAUSB device is not connected"); + kref_put(&dev->refcount, mausb_release_ma_dev_async); + return; + } + mausb_hcd_disconnect(dev->port_number, dev->dev_type, dev->dev_speed); + + if (dev->dev_type == USB30HUB) + mausb_hcd_disconnect(dev->port_number, USB20HUB, HIGH_SPEED); +} + +static void mausb_hcd_disconnect_event(struct work_struct *work) +{ + struct mausb_device *ma_dev = container_of(work, struct mausb_device, + hcd_disconnect_work); + + mausb_disconnect_ma_dev(ma_dev); +} + +static void mausb_delete_madev(struct work_struct *work) +{ + struct mausb_device *dev = container_of(work, struct mausb_device, + madev_delete_work); + struct mausb_event event; + struct completion completion; + struct completion *user_event; + struct mausb_completion mausb_completion; + long status; + unsigned long timeout = msecs_to_jiffies(MAUSB_DELETE_MADEV_TIMEOUT_MS); + + mausb_pr_info("Deleting MAUSB device madev_addr=%d", dev->madev_addr); + + del_timer_sync(&dev->connection_timer); + + /* Client IS responsive */ + if (!atomic_read(&dev->unresponsive_client)) { + memset(&event, 0, sizeof(event)); + event.type = MAUSB_EVENT_TYPE_DELETE_MA_DEV; + event.mgmt.delete_ma_dev.device_id = dev->id; + event.mgmt.delete_ma_dev.event_id = mausb_event_id(dev); + + init_completion(&completion); + mausb_completion.completion_event = &completion; + mausb_completion.event_id = event.mgmt.delete_ma_dev.event_id; + mausb_completion.mausb_event = &event; + + mausb_insert_event(dev, &mausb_completion); + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) { + mausb_remove_event(dev, &mausb_completion); + mausb_pr_err("Ring buffer full, enqueue failed"); + return; + } + mausb_pr_debug("Deleting MAUSB device..."); + + status = wait_for_completion_interruptible_timeout(&completion, + timeout); + + mausb_pr_debug("Deleting MAUSB device event finished with %ld", + status); + + mausb_remove_event(dev, &mausb_completion); + + user_event = &dev->user_finished_event; + + status = wait_for_completion_interruptible_timeout(user_event, + timeout); + mausb_pr_info("User event finished with %ld", status); + } + + flush_workqueue(dev->workq); + destroy_workqueue(dev->workq); + + mausb_clear_hcd_madev(dev->port_number); + + mausb_ring_buffer_cleanup(dev->ring_buffer); + mausb_ring_buffer_destroy(dev->ring_buffer); + + mausb_remove_madev_from_list(dev->madev_addr); + + put_net(dev->net_ns); + + kfree(dev->ring_buffer); + kfree(dev); + mausb_signal_empty_mss(); + + mausb_pr_info("MAUSB device deleted. Version=%s", MAUSB_DRIVER_VERSION); +} + +static void mausb_ping_work(struct work_struct *work) +{ + struct mausb_device *dev = container_of(work, struct mausb_device, + ping_work); + int status = 0; + + if (mausb_start_connection_timer(dev) < 0) { + mausb_pr_err("Device disconnecting due to session timeout madev_addr=%d", + dev->madev_addr); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + return; + } + + status = mausb_ping_event_to_user(dev); + + if (status < 0) { + mausb_pr_err("Ring buffer full"); + return; + } +} + +static void mausb_heartbeat_work(struct work_struct *work) +{ + struct mausb_device *dev = container_of(work, struct mausb_device, + heartbeat_work); + + mausb_pr_err("Device disconnecting - app is unresponsive"); + atomic_set(&dev->unresponsive_client, 1); + mausb_complete_urbs_from_tree(); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); +} + +static void mausb_connection_timer_func(struct timer_list *timer) +{ + struct mausb_device *dev = container_of(timer, struct mausb_device, + connection_timer); + + queue_work(dev->workq, &dev->ping_work); +} + +static void mausb_heartbeat_timer_func(struct timer_list *timer) +{ + unsigned long flags = 0; + struct mausb_device *dev = NULL; + + if (mausb_start_heartbeat_timer() < 0) { + mausb_pr_err("Devices disconnecting - app is unresponsive"); + spin_lock_irqsave(&mss.lock, flags); + + /* Reset connected clients */ + mss.client_connected = false; + mss.missed_heartbeats = 0; + + list_for_each_entry(dev, &mss.madev_list, list_entry) { + mausb_pr_debug("Enqueue heartbeat_work madev_addr=%x", + dev->madev_addr); + queue_work(dev->workq, &dev->heartbeat_work); + } + + complete(&mss.client_stopped); + spin_unlock_irqrestore(&mss.lock, flags); + } +} + +static struct mausb_device *mausb_create_madev(struct mausb_device_address + dev_addr, u8 madev_address, + int *status) +{ + struct mausb_device *dev; + unsigned long flags = 0; + char workq_name[16]; + struct workqueue_struct *workq; + + memset(workq_name, 0, sizeof(workq_name)); + sprintf(workq_name, "%x", madev_address); + strcat(workq_name, "_madev_workq"); + + mausb_pr_debug("madev_workq_name = %s", workq_name); + + workq = alloc_ordered_workqueue(workq_name, WQ_MEM_RECLAIM); + if (!workq) { + mausb_pr_alert("Could not allocate workqueue!"); + *status = -ENOMEM; + return NULL; + } + + spin_lock_irqsave(&mss.lock, flags); + + if (mss.deinit_in_progress) { + spin_unlock_irqrestore(&mss.lock, flags); + mausb_pr_alert("Device creating failed - mss deinit in progress"); + flush_workqueue(workq); + destroy_workqueue(workq); + *status = -ESHUTDOWN; + return NULL; + } + + dev = mausb_get_dev_from_addr_unsafe(madev_address); + if (dev) { + spin_unlock_irqrestore(&mss.lock, flags); + mausb_pr_debug("MAUSB device already connected, madev_address=%x", + madev_address); + flush_workqueue(workq); + destroy_workqueue(workq); + *status = -EEXIST; + return NULL; + } + + dev = kzalloc(sizeof(*dev), GFP_ATOMIC); + + if (!dev) { + spin_unlock_irqrestore(&mss.lock, flags); + mausb_pr_alert("Could not allocate MAUSB device!"); + flush_workqueue(workq); + destroy_workqueue(workq); + *status = -ENOMEM; + return NULL; + } + + mausb_pr_info("Create MAUSB device. Version=%s", MAUSB_DRIVER_VERSION); + + dev->workq = workq; + + INIT_WORK(&dev->work, mausb_hpal_kernel_work); + INIT_WORK(&dev->socket_disconnect_work, mausb_socket_disconnect_event); + INIT_WORK(&dev->hcd_disconnect_work, mausb_hcd_disconnect_event); + INIT_WORK(&dev->madev_delete_work, mausb_delete_madev); + INIT_WORK(&dev->ping_work, mausb_ping_work); + INIT_WORK(&dev->heartbeat_work, mausb_heartbeat_work); + + kref_init(&dev->refcount); + + dev->event_id = 0; + spin_lock_init(&dev->event_id_lock); + + INIT_LIST_HEAD(&dev->completion_events); + spin_lock_init(&dev->completion_events_lock); + spin_lock_init(&dev->num_of_user_events_lock); + spin_lock_init(&dev->connection_timer_lock); + + init_completion(&dev->user_finished_event); + atomic_set(&dev->unresponsive_client, 0); + + timer_setup(&dev->connection_timer, mausb_connection_timer_func, 0); + + dev->dev_addr = dev_addr; + dev->madev_addr = madev_address; + dev->net_ns = get_net(current->nsproxy->net_ns); + + if (!list_empty(&mss.available_ring_buffers)) { + dev->ring_buffer = container_of(mss.available_ring_buffers.next, + struct mausb_ring_buffer, + list_entry); + list_del(mss.available_ring_buffers.next); + } else { + mausb_pr_alert("Ring buffer for mausb device is not availbale!"); + } + + list_add_tail(&dev->list_entry, &mss.madev_list); + + reinit_completion(&mss.empty); + + spin_unlock_irqrestore(&mss.lock, flags); + + return dev; +} + +void mausb_release_ma_dev_async(struct kref *kref) +{ + struct mausb_device *dev = container_of(kref, struct mausb_device, + refcount); + + mausb_pr_info("Scheduling work for MAUSB device to be deleted"); + + schedule_work(&dev->madev_delete_work); +} + +int mausb_initiate_dev_connection(struct mausb_device_address dev_addr, + u8 madev_address) +{ + int error = 0; + struct mausb_device *dev; + unsigned long flags = 0; + + spin_lock_irqsave(&mss.lock, flags); + dev = mausb_get_dev_from_addr_unsafe(madev_address); + spin_unlock_irqrestore(&mss.lock, flags); + + if (dev) { + mausb_pr_debug("MAUSB device already connected, madev_address=%x", + madev_address); + return -EEXIST; + } + + dev = mausb_create_madev(dev_addr, madev_address, &error); + + if (!dev) + return error; + + mausb_pr_info("New MAUSB device created madev_addr=%d", madev_address); + + error = mausb_init_ip_ctx(&dev->mgmt_channel, dev->net_ns, + dev->dev_addr.ip.address.ip4, + dev->dev_addr.ip.port.management, dev, + mausb_ip_callback, MAUSB_MGMT_CHANNEL); + if (error) { + mausb_pr_err("Mgmt ip context init failed: error=%d", error); + kref_put(&dev->refcount, mausb_release_ma_dev_async); + return error; + } + + mausb_ip_connect_async(dev->mgmt_channel); + + return 0; +} + +void mausb_on_madev_connected(struct mausb_device *dev) +{ + struct mausb_event mausb_event; + + mausb_dev_reset_req_event(&mausb_event); + mausb_enqueue_event_to_user(dev, &mausb_event); +} + +int mausb_enqueue_event_from_user(u8 madev_addr, u16 num_of_events, + u16 num_of_completed) +{ + unsigned long flags; + struct mausb_device *dev; + + spin_lock_irqsave(&mss.lock, flags); + dev = mausb_get_dev_from_addr_unsafe(madev_addr); + + if (!dev) { + spin_unlock_irqrestore(&mss.lock, flags); + return -EINVAL; + } + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + dev->num_of_user_events += num_of_events; + dev->num_of_completed_events += num_of_completed; + spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags); + queue_work(dev->workq, &dev->work); + spin_unlock_irqrestore(&mss.lock, flags); + + return 0; +} + +int mausb_enqueue_event_to_user(struct mausb_device *dev, + struct mausb_event *event) +{ + int status; + + event->madev_addr = dev->madev_addr; + status = mausb_ring_buffer_put(dev->ring_buffer, event); + if (status < 0) { + mausb_pr_err("Ring buffer operation failed"); + mausb_cleanup_ring_buffer_event(event); + return status; + } + + mausb_notify_ring_events(dev->ring_buffer); + mausb_pr_debug("User-space notification sent."); + + return 0; +} + +int mausb_data_req_enqueue_event(struct mausb_device *dev, u16 ep_handle, + struct urb *request) +{ + int status; + struct mausb_event mausb_event; + + mausb_event.type = MAUSB_EVENT_TYPE_SEND_DATA_MSG; + mausb_event.status = 0; + + mausb_event.data.transfer_type = + mausb_transfer_type_from_usb(&request->ep->desc); + mausb_event.data.device_id = dev->id; + mausb_event.data.ep_handle = ep_handle; + mausb_event.data.urb = (u64)request; + mausb_event.data.setup_packet = + (usb_endpoint_xfer_control(&request->ep->desc) && + request->setup_packet); + mausb_event.data.transfer_size = request->transfer_buffer_length; + mausb_event.data.direction = (usb_urb_dir_in(request) ? + MAUSB_DATA_MSG_DIRECTION_IN : + MAUSB_DATA_MSG_DIRECTION_OUT); + mausb_event.data.transfer_size += + ((mausb_event.data.direction == MAUSB_DATA_MSG_DIRECTION_OUT && + mausb_event.data.setup_packet) ? + MAUSB_CONTROL_SETUP_SIZE : 0); + mausb_event.data.rem_transfer_size = mausb_event.data.transfer_size; + mausb_event.data.transfer_flags = request->transfer_flags; + mausb_event.data.transfer_eot = false; + mausb_event.data.isoch_seg_num = (u32)request->number_of_packets; + mausb_event.data.recv_buf = 0; + mausb_event.data.payload_size = + (usb_endpoint_xfer_isoc(&request->ep->desc) && + usb_endpoint_dir_out(&request->ep->desc)) ? + (request->iso_frame_desc[request->number_of_packets - 1] + .offset + + request->iso_frame_desc[request->number_of_packets - 1] + .length) : 0; + + if (mausb_event.data.setup_packet) { + memcpy(mausb_event.data.hdr_ack, request->setup_packet, + MAUSB_CONTROL_SETUP_SIZE); + memcpy(shift_ptr(mausb_event.data.hdr_ack, + MAUSB_CONTROL_SETUP_SIZE), + &request->dev->route, sizeof(request->dev->route)); + } + + status = mausb_enqueue_event_to_user(dev, &mausb_event); + if (status < 0) + mausb_pr_err("Failed to enqueue event to user-space ep_handle=%#x, status=%d", + mausb_event.data.ep_handle, status); + + return status; +} + +void mausb_complete_request(struct urb *urb, u32 actual_length, int status) +{ + mausb_hcd_urb_complete(urb, actual_length, status); +} + +int mausb_signal_event(struct mausb_device *dev, + struct mausb_event *event, u64 event_id) +{ + unsigned long flags; + struct mausb_completion *mausb_completion; + + spin_lock_irqsave(&dev->completion_events_lock, flags); + list_for_each_entry(mausb_completion, &dev->completion_events, + list_entry) { + if (mausb_completion->event_id == event_id) { + memcpy(mausb_completion->mausb_event, event, + sizeof(*event)); + complete(mausb_completion->completion_event); + spin_unlock_irqrestore(&dev->completion_events_lock, + flags); + return 0; + } + } + spin_unlock_irqrestore(&dev->completion_events_lock, flags); + + return -ETIMEDOUT; +} + +static int mausb_start_connection_timer(struct mausb_device *dev) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&dev->connection_timer_lock, flags); + + if (++dev->receive_failures_num > MAUSB_MAX_RECEIVE_FAILURES) { + mausb_pr_err("Missed more than %d ping responses", + MAUSB_MAX_RECEIVE_FAILURES); + spin_unlock_irqrestore(&dev->connection_timer_lock, flags); + return -ETIMEDOUT; + } + + mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000)); + + spin_unlock_irqrestore(&dev->connection_timer_lock, flags); + + return 0; +} + +void mausb_reset_connection_timer(struct mausb_device *dev) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&dev->connection_timer_lock, flags); + dev->receive_failures_num = 0; + + mod_timer(&dev->connection_timer, jiffies + msecs_to_jiffies(1000)); + + spin_unlock_irqrestore(&dev->connection_timer_lock, flags); +} + +static int mausb_start_heartbeat_timer(void) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&mss.lock, flags); + if (++mss.missed_heartbeats > MAUSB_MAX_MISSED_HEARTBEATS) { + mausb_pr_err("Missed more than %d heartbeats", + MAUSB_MAX_MISSED_HEARTBEATS); + spin_unlock_irqrestore(&mss.lock, flags); + return -ETIMEDOUT; + } + + spin_unlock_irqrestore(&mss.lock, flags); + mod_timer(&mss.heartbeat_timer, + jiffies + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS)); + + return 0; +} + +void mausb_reset_heartbeat_cnt(void) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&mss.lock, flags); + mss.missed_heartbeats = 0; + spin_unlock_irqrestore(&mss.lock, flags); +} + +static void mausb_execute_urb_dequeue(struct work_struct *dequeue_work) +{ + struct mausb_urb_ctx *urb_ctx = + container_of(dequeue_work, struct mausb_urb_ctx, work); + struct urb *urb = urb_ctx->urb; + struct mausb_endpoint_ctx *ep_ctx; + struct mausb_device *ma_dev; + struct mausb_event mausb_event; + int status = 0; + + ep_ctx = urb->ep->hcpriv; + ma_dev = ep_ctx->ma_dev; + + if (atomic_read(&ma_dev->unresponsive_client)) { + mausb_pr_err("Client is not responsive anymore - finish urb immediately urb=%p, ep_handle=%#x, dev_handle=%#x", + urb, ep_ctx->ep_handle, ep_ctx->dev_handle); + goto complete_urb; + } + + mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x", + urb, ep_ctx->ep_handle, ep_ctx->dev_handle); + + if (!usb_endpoint_xfer_isoc(&urb->ep->desc)) { + status = mausb_canceltransfer_event_to_user(ep_ctx->ma_dev, + ep_ctx->dev_handle, + ep_ctx->ep_handle, + (u64)urb); + if (status < 0) { + mausb_pr_err("Failed to enqueue cancel transfer to user"); + goto complete_urb; + } + } + + memset(&mausb_event, 0, sizeof(mausb_event)); + + mausb_event.type = MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER; + mausb_event.status = 0; + + mausb_event.data.transfer_type = + mausb_transfer_type_from_usb(&urb->ep->desc); + mausb_event.data.device_id = ma_dev->id; + mausb_event.data.ep_handle = ep_ctx->ep_handle; + mausb_event.data.urb = (u64)urb; + mausb_event.data.direction = (usb_urb_dir_in(urb) ? + MAUSB_DATA_MSG_DIRECTION_IN : + MAUSB_DATA_MSG_DIRECTION_OUT); + + status = mausb_enqueue_event_to_user(ep_ctx->ma_dev, &mausb_event); + if (status < 0) { + mausb_pr_alert("Failed to enqueue event to user-space ep_handle=%#x, status=%d", + mausb_event.data.ep_handle, status); + goto complete_urb; + } + + if (!mausb_return_urb_ctx_to_tree(urb_ctx, false)) { + mausb_pr_alert("Failed to insert in tree urb=%p ep_handle=%#x, status=%d", + urb, mausb_event.data.ep_handle, status); + goto complete_urb; + } + + return; + +complete_urb: + + /* 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); +} + +void mausb_initialize_mss(void) +{ + spin_lock_init(&mss.lock); + INIT_LIST_HEAD(&mss.madev_list); + INIT_LIST_HEAD(&mss.available_ring_buffers); + + init_completion(&mss.empty); + complete(&mss.empty); + init_completion(&mss.rings_events.mausb_ring_has_events); + atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 0); + mss.deinit_in_progress = false; + mss.ring_buffer_id = 0; + mss.client_connected = false; + mss.missed_heartbeats = 0; + init_completion(&mss.client_stopped); + atomic_set(&mss.num_of_transitions_to_sleep, 0); + + timer_setup(&mss.heartbeat_timer, mausb_heartbeat_timer_func, 0); +} + +void mausb_deinitialize_mss(void) +{ + struct mausb_device *dev = NULL; + unsigned long flags = 0; + unsigned long timeout = + msecs_to_jiffies(MAUSB_CLIENT_STOPPED_TIMEOUT_MS); + + spin_lock_irqsave(&mss.lock, flags); + + mss.deinit_in_progress = true; + + list_for_each_entry(dev, &mss.madev_list, list_entry) { + mausb_pr_debug("Enqueue mausb_hcd_disconnect_work madev_addr=%x", + dev->madev_addr); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + spin_unlock_irqrestore(&mss.lock, flags); + + wait_for_completion(&mss.empty); + mausb_pr_debug("Waiting for completion on disconnect_event ended"); + mausb_stop_ring_events(); + + timeout = wait_for_completion_timeout(&mss.client_stopped, timeout); + mausb_pr_info("Remaining time after waiting for stopping client %ld", + timeout); +} + +int mausb_register_power_state_listener(void) +{ + mausb_pr_info("Registering power states listener"); + + mhcd->power_state_listener.notifier_call = mausb_power_state_cb; + return register_pm_notifier(&mhcd->power_state_listener); +} + +void mausb_unregister_power_state_listener(void) +{ + mausb_pr_info("Un-registering power states listener"); + + unregister_pm_notifier(&mhcd->power_state_listener); +} + +static int mausb_power_state_cb(struct notifier_block *nb, unsigned long action, + void *data) +{ + unsigned long flags = 0; + struct mausb_device *dev = NULL; + + mausb_pr_info("Power state callback action = %ld", action); + if (action == PM_SUSPEND_PREPARE || action == PM_HIBERNATION_PREPARE) { + /* Stop heartbeat timer */ + del_timer_sync(&mss.heartbeat_timer); + mausb_pr_info("Saving state before sleep"); + spin_lock_irqsave(&mss.lock, flags); + if (!list_empty(&mss.madev_list)) + atomic_inc(&mss.num_of_transitions_to_sleep); + + list_for_each_entry(dev, &mss.madev_list, list_entry) { + mausb_pr_info("Enqueue heartbeat_work madev_addr=%x", + dev->madev_addr); + queue_work(dev->workq, &dev->heartbeat_work); + } + + spin_unlock_irqrestore(&mss.lock, flags); + } else if (action == PM_POST_SUSPEND || action == PM_POST_HIBERNATION) { + mausb_reset_heartbeat_cnt(); + /* Start hearbeat timer */ + mod_timer(&mss.heartbeat_timer, jiffies + + msecs_to_jiffies(MAUSB_HEARTBEAT_TIMEOUT_MS)); + } + return NOTIFY_OK; +} + +struct mausb_device *mausb_get_dev_from_addr_unsafe(u8 madev_addr) +{ + struct mausb_device *dev = NULL; + + list_for_each_entry(dev, &mss.madev_list, list_entry) { + if (dev->madev_addr == madev_addr) + return dev; + } + + return NULL; +} + +static void mausb_remove_madev_from_list(u8 madev_addr) +{ + unsigned long flags = 0; + struct mausb_device *ma_dev, *tmp = NULL; + + spin_lock_irqsave(&mss.lock, flags); + + list_for_each_entry_safe(ma_dev, tmp, &mss.madev_list, list_entry) { + if (ma_dev->madev_addr == madev_addr) { + list_del(&ma_dev->list_entry); + break; + } + } + + if (list_empty(&mss.madev_list)) + reinit_completion(&mss.rings_events.mausb_ring_has_events); + + spin_unlock_irqrestore(&mss.lock, flags); +} + +static void mausb_signal_empty_mss(void) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&mss.lock, flags); + if (list_empty(&mss.madev_list)) + complete(&mss.empty); + spin_unlock_irqrestore(&mss.lock, flags); +} diff --git a/drivers/usb/mausb_host/src/hpal/isoch_in.c b/drivers/usb/mausb_host/src/hpal/isoch_in.c new file mode 100644 index 000000000000..446dd9ccd506 --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/isoch_in.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hpal/isoch_in.h" + +#include +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "hpal/hpal.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_logs.h" + +static inline u32 +__mausb_isoch_prepare_read_size_block(struct ma_usb_hdr_isochreadsizeblock_std * + isoch_readsize_block, struct urb *urb) +{ + u32 i; + u32 number_of_packets = (u32)urb->number_of_packets; + + if (number_of_packets == 0) + return 0; + + isoch_readsize_block->service_intervals = number_of_packets; + isoch_readsize_block->max_segment_length = + (u32)urb->iso_frame_desc[0].length; + + for (i = 0; i < number_of_packets; ++i) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + return sizeof(struct ma_usb_hdr_isochreadsizeblock_std); +} + +int mausb_send_isoch_in_msg(struct mausb_device *dev, struct mausb_event *event) +{ + u32 read_size_block_length = 0; + struct mausb_kvec_data_wrapper data_to_send; + struct kvec kvec[MAUSB_ISOCH_IN_KVEC_NUM]; + struct ma_usb_hdr_isochtransfer_optional opt_isoch_hdr; + struct ma_usb_hdr_isochreadsizeblock_std isoch_readsize_block; + struct ma_usb_hdr_common *hdr = + (struct ma_usb_hdr_common *)event->data.hdr; + struct urb *urb = (struct urb *)event->data.urb; + enum mausb_channel channel; + + data_to_send.kvec_num = 0; + data_to_send.length = 0; + + /* Prepare transfer header kvec */ + kvec[0].iov_base = event->data.hdr; + kvec[0].iov_len = MAUSB_TRANSFER_HDR_SIZE; + data_to_send.length += (u32)kvec[0].iov_len; + data_to_send.kvec_num++; + + /* Prepare optional header kvec */ + opt_isoch_hdr.timestamp = MA_USB_TRANSFER_RESERVED; + opt_isoch_hdr.mtd = MA_USB_TRANSFER_RESERVED; + + kvec[1].iov_base = &opt_isoch_hdr; + kvec[1].iov_len = sizeof(struct ma_usb_hdr_isochtransfer_optional); + data_to_send.length += (u32)kvec[1].iov_len; + data_to_send.kvec_num++; + + /* Prepare read size blocks */ + read_size_block_length = + __mausb_isoch_prepare_read_size_block(&isoch_readsize_block, + urb); + if (read_size_block_length > 0) { + kvec[2].iov_base = &isoch_readsize_block; + kvec[2].iov_len = read_size_block_length; + data_to_send.length += (u32)kvec[2].iov_len; + data_to_send.kvec_num++; + } + + hdr->length = (u16)data_to_send.length; + data_to_send.kvec = kvec; + + channel = mausb_transfer_type_to_channel(event->data.transfer_type); + return mausb_send_data(dev, channel, &data_to_send); +} + +static void __mausb_process_in_isoch_short_resp(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + struct mausb_urb_ctx *urb_ctx) +{ + u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ? + sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0; + struct ma_usb_hdr_isochdatablock_short *data_block_hdr = + (struct ma_usb_hdr_isochdatablock_short *) + shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr), + opt_hdr_shift); + u8 *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers * + sizeof(*data_block_hdr)); + u8 *end_of_packet = shift_ptr(hdr, hdr->length); + struct urb *urb = urb_ctx->urb; + int i; + + if (isoch_data >= end_of_packet) { + mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x", + event->data.ep_handle); + return; + } + + for (i = 0; i < hdr->data.headers; ++i) { + u16 seg_num = data_block_hdr[i].segment_number; + u16 seg_size = data_block_hdr[i].block_length; + + if (seg_num >= urb->number_of_packets) { + mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, urb.number_of_packets=%d", + event->data.ep_handle, seg_num, + urb->number_of_packets); + break; + } + + if (seg_size > urb->iso_frame_desc[seg_num].length) { + mausb_pr_err("Block to long for segment: ep_handle=%#x", + event->data.ep_handle); + break; + } + + if (shift_ptr(isoch_data, seg_size) > end_of_packet) { + mausb_pr_err("End of segment after enf of packet: ep_handle=%#x", + event->data.ep_handle); + break; + } + + mausb_reset_data_iterator(&urb_ctx->iterator); + mausb_data_iterator_seek(&urb_ctx->iterator, + urb->iso_frame_desc[seg_num].offset); + mausb_data_iterator_write(&urb_ctx->iterator, isoch_data, + seg_size); + + isoch_data = shift_ptr(isoch_data, seg_size); + + urb->iso_frame_desc[seg_num].actual_length = seg_size; + urb->iso_frame_desc[seg_num].status = 0; + } +} + +static void __mausb_process_in_isoch_std_resp(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + struct mausb_urb_ctx *urb_ctx) +{ + u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ? + sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0; + struct ma_usb_hdr_isochdatablock_std *data_block_hdr = + (struct ma_usb_hdr_isochdatablock_std *) + shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr), + opt_hdr_shift); + u8 *isoch_data = + shift_ptr(data_block_hdr, hdr->data.headers * + sizeof(struct ma_usb_hdr_isochdatablock_std)); + u8 *end_of_packet = shift_ptr(hdr, hdr->length); + struct urb *urb = (struct urb *)event->data.urb; + int i; + + if (isoch_data >= end_of_packet) { + mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x", + event->data.ep_handle); + return; + } + + for (i = 0; i < hdr->data.headers; ++i) { + u16 seg_num = data_block_hdr[i].segment_number; + u16 seg_len = data_block_hdr[i].segment_length; + u16 block_len = data_block_hdr[i].block_length; + + if (seg_num >= urb->number_of_packets) { + mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, number_of_packets=%d", + event->data.ep_handle, seg_num, + urb->number_of_packets); + break; + } + + if (block_len > urb->iso_frame_desc[seg_num].length - + urb->iso_frame_desc[seg_num].actual_length) { + mausb_pr_err("Block too long for segment: ep_handle=%#x", + event->data.ep_handle); + break; + } + + if (shift_ptr(isoch_data, block_len) > + end_of_packet) { + mausb_pr_err("End of fragment after end of packet: ep_handle=%#x", + event->data.ep_handle); + break; + } + + mausb_reset_data_iterator(&urb_ctx->iterator); + mausb_data_iterator_seek(&urb_ctx->iterator, + urb->iso_frame_desc[seg_num].offset + + data_block_hdr[i].fragment_offset); + mausb_data_iterator_write(&urb_ctx->iterator, + isoch_data, block_len); + isoch_data = shift_ptr(isoch_data, block_len); + + urb->iso_frame_desc[seg_num].actual_length += block_len; + + if (urb->iso_frame_desc[seg_num].actual_length == seg_len) + urb->iso_frame_desc[seg_num].status = 0; + } +} + +int mausb_receive_isoch_in_data(struct mausb_device *dev, + struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx) +{ + struct ma_usb_hdr_common *common_hdr = + (struct ma_usb_hdr_common *)event->data.recv_buf; + struct ma_usb_hdr_transfer *transfer_hdr = + mausb_get_data_transfer_hdr(common_hdr); + + if (!(common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK)) { + /* Short ISO headers response */ + __mausb_process_in_isoch_short_resp(event, common_hdr, urb_ctx); + } else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) & + MA_USB_DATA_IFLAGS_HDR_FMT_STD) { + /* Standard ISO headers response */ + __mausb_process_in_isoch_std_resp(event, common_hdr, urb_ctx); + } else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) & + MA_USB_DATA_IFLAGS_HDR_FMT_LONG) { + /* Long ISO headers response */ + mausb_pr_warn("Long isoc headers in response: ep_handle=%#x, req_id=%#x", + event->data.ep_handle, transfer_hdr->req_id); + } else { + /* Error */ + mausb_pr_err("Isoc header error in response: ep_handle=%#x, req_id=%#x", + event->data.ep_handle, transfer_hdr->req_id); + } + + return 0; +} diff --git a/drivers/usb/mausb_host/src/hpal/isoch_out.c b/drivers/usb/mausb_host/src/hpal/isoch_out.c new file mode 100644 index 000000000000..ee2f37653efe --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/isoch_out.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hpal/isoch_out.h" + +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "hpal/hpal.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_logs.h" + +static inline u32 +__mausb_calculate_isoch_common_header_size(u32 num_of_segments) +{ + return MAUSB_ISOCH_TRANSFER_HDR_SIZE + + MAUSB_ISOCH_STANDARD_FORMAT_SIZE * num_of_segments; +} + +static struct ma_usb_hdr_common * +__mausb_create_isoch_out_transfer_packet(struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx, + u16 payload_size, u32 seq_n, + u32 start_of_segments, + u32 number_of_segments) +{ + struct ma_usb_hdr_common *hdr; + struct ma_usb_hdr_isochtransfer *hdr_isochtransfer; + struct ma_usb_hdr_isochdatablock_std *isoc_header_std; + struct ma_usb_hdr_isochtransfer_optional *hdr_opt_isochtransfer; + struct urb *urb = (struct urb *)event->data.urb; + void *isoc_headers = NULL; + u32 length; + u16 i; + unsigned long block_length; + u32 number_of_packets = (u32)event->data.isoch_seg_num; + u32 size_of_request = + __mausb_calculate_isoch_common_header_size(number_of_segments); + + hdr = kzalloc(size_of_request, GFP_KERNEL); + if (!hdr) + return NULL; + + hdr->version = MA_USB_HDR_VERSION_1_0; + hdr->ssid = event->data.mausb_ssid; + hdr->flags = MA_USB_HDR_FLAGS_HOST; + hdr->dev_addr = event->data.mausb_address; + hdr->handle.epv = event->data.ep_handle; + hdr->data.status = MA_USB_HDR_STATUS_NO_ERROR; + hdr->data.eps = MAUSB_TRANSFER_RESERVED; + hdr->data.t_flags = (u8)(usb_endpoint_type(&urb->ep->desc) << 3); + + isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE); + + for (i = (u16)start_of_segments; + i < number_of_segments + start_of_segments; ++i) { + block_length = i < number_of_packets - 1 ? + urb->iso_frame_desc[i + 1].offset - + urb->iso_frame_desc[i].offset : + mausb_data_iterator_length(&urb_ctx->iterator) - + urb->iso_frame_desc[i].offset; + + urb->iso_frame_desc[i].status = MA_USB_HDR_STATUS_UNSUCCESSFUL; + isoc_header_std = (struct ma_usb_hdr_isochdatablock_std *) + shift_ptr(isoc_headers, + (u64)MAUSB_ISOCH_STANDARD_FORMAT_SIZE * + (i - start_of_segments)); + isoc_header_std->block_length = (u16)block_length; + isoc_header_std->segment_number = i; + isoc_header_std->s_flags = 0; + isoc_header_std->segment_length = (u16)block_length; + isoc_header_std->fragment_offset = 0; + } + + length = __mausb_calculate_isoch_common_header_size(number_of_segments); + + hdr->flags |= MA_USB_HDR_FLAGS_TIMESTAMP; + hdr->type = (u8)MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER); + hdr->data.headers = (u16)number_of_segments; + hdr->data.i_flags = MA_USB_DATA_IFLAGS_HDR_FMT_STD | + MA_USB_DATA_IFLAGS_ASAP; + hdr_opt_isochtransfer = mausb_hdr_isochtransfer_optional_hdr(hdr); + hdr_isochtransfer = mausb_get_isochtransfer_hdr(hdr); + hdr_isochtransfer->req_id = event->data.req_id; + hdr_isochtransfer->seq_n = seq_n; + hdr_isochtransfer->segments = number_of_packets; + + hdr_isochtransfer->presentation_time = MA_USB_TRANSFER_RESERVED; + + hdr_opt_isochtransfer->timestamp = MA_USB_TRANSFER_RESERVED; + hdr_opt_isochtransfer->mtd = MA_USB_TRANSFER_RESERVED; + + hdr->length = (u16)length + payload_size; + + return hdr; +} + +static int mausb_add_data_chunk(void *buffer, u32 buffer_size, + struct list_head *chunks_list) +{ + struct mausb_payload_chunk *data_chunk = NULL; + + data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL); + if (!data_chunk) + return -ENOMEM; + + /* Initialize data chunk for MAUSB header and add it to chunks list */ + INIT_LIST_HEAD(&data_chunk->list_entry); + data_chunk->kvec.iov_base = buffer; + data_chunk->kvec.iov_len = buffer_size; + list_add_tail(&data_chunk->list_entry, chunks_list); + return 0; +} + +static int mausb_init_header_data_chunk(struct ma_usb_hdr_common *common_hdr, + struct list_head *chunks_list, + u32 *num_of_data_chunks, + u32 num_of_packets) +{ + u32 header_size = + __mausb_calculate_isoch_common_header_size(num_of_packets); + int status = mausb_add_data_chunk(common_hdr, header_size, chunks_list); + + if (!status) + ++(*num_of_data_chunks); + + return status; +} + +static int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data, + struct list_head *chunks_list, + u32 num_of_data_chunks) +{ + struct mausb_payload_chunk *data_chunk = NULL, *tmp = NULL; + u32 current_kvec = 0; + + data->length = 0; + data->kvec = kcalloc(num_of_data_chunks, sizeof(struct kvec), + GFP_KERNEL); + if (!data->kvec) + return -ENOMEM; + + list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) { + data->kvec[current_kvec].iov_base = + data_chunk->kvec.iov_base; + data->kvec[current_kvec].iov_len = + data_chunk->kvec.iov_len; + ++data->kvec_num; + data->length += data_chunk->kvec.iov_len; + ++current_kvec; + } + return 0; +} + +static void mausb_cleanup_chunks_list(struct list_head *chunks_list) +{ + struct mausb_payload_chunk *data_chunk = NULL, *tmp = NULL; + + list_for_each_entry_safe(data_chunk, tmp, chunks_list, list_entry) { + list_del(&data_chunk->list_entry); + kfree(data_chunk); + } +} + +static +int mausb_prepare_isoch_out_transfer_packet(struct ma_usb_hdr_common *hdr, + struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx, + struct mausb_kvec_data_wrapper * + result_data_wrapper) +{ + u32 num_of_data_chunks = 0; + u32 num_of_payload_data_chunks = 0; + u32 segment_number = event->data.isoch_seg_num; + u32 payload_data_size; + struct list_head chunks_list; + struct list_head payload_data_chunks; + int status = 0; + + INIT_LIST_HEAD(&chunks_list); + + /* Initialize data chunk for MAUSB header and add it to chunks list */ + if (mausb_init_header_data_chunk(hdr, &chunks_list, &num_of_data_chunks, + segment_number) < 0) { + status = -ENOMEM; + goto cleanup_data_chunks; + } + + /* Get data chunks for data payload to send */ + INIT_LIST_HEAD(&payload_data_chunks); + payload_data_size = hdr->length - + __mausb_calculate_isoch_common_header_size(segment_number); + + if (mausb_data_iterator_read(&urb_ctx->iterator, payload_data_size, + &payload_data_chunks, + &num_of_payload_data_chunks) < 0) { + mausb_pr_err("Data iterator read failed"); + status = -ENOMEM; + goto cleanup_data_chunks; + } + + list_splice_tail(&payload_data_chunks, &chunks_list); + num_of_data_chunks += num_of_payload_data_chunks; + + /* Map all data chunks to data wrapper */ + if (mausb_init_data_wrapper(result_data_wrapper, &chunks_list, + num_of_data_chunks) < 0) { + mausb_pr_err("Data wrapper init failed"); + status = -ENOMEM; + goto cleanup_data_chunks; + } + +cleanup_data_chunks: + mausb_cleanup_chunks_list(&chunks_list); + return status; +} + +static int mausb_create_and_send_isoch_transfer_req(struct mausb_device *dev, + struct mausb_event *event, + struct mausb_urb_ctx + *urb_ctx, u32 *seq_n, + u32 payload_size, + u32 start_of_segments, + u32 number_of_segments) +{ + struct ma_usb_hdr_common *hdr; + struct mausb_kvec_data_wrapper data_to_send; + int status; + enum mausb_channel channel; + + hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx, + (u16)payload_size, + *seq_n, + start_of_segments, + number_of_segments); + if (!hdr) { + mausb_pr_alert("Isoch transfer packet alloc failed"); + return -ENOMEM; + } + *seq_n = (*seq_n + 1) % (MA_USB_TRANSFER_SEQN_MAX + 1); + + status = mausb_prepare_isoch_out_transfer_packet(hdr, event, urb_ctx, + &data_to_send); + if (status < 0) { + mausb_pr_alert("Failed to prepare transfer packet"); + kfree(hdr); + return status; + } + + channel = mausb_transfer_type_to_channel(event->data.transfer_type); + status = mausb_send_data(dev, channel, &data_to_send); + + kfree(hdr); + kfree(data_to_send.kvec); + + return status; +} + +static inline int __mausb_send_isoch_out_packet(struct mausb_device *dev, + struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx, + u32 *seq_n, + u32 *starting_segments, + u32 *rem_transfer_buf, + u32 *payload_size, u32 index) +{ + int status = mausb_create_and_send_isoch_transfer_req(dev, event, + urb_ctx, seq_n, *payload_size, + *starting_segments, + index - *starting_segments); + if (status < 0) { + mausb_pr_err("ISOCH transfer request create and send failed"); + return status; + } + *starting_segments = index; + *rem_transfer_buf = MAX_ISOCH_ASAP_PACKET_SIZE; + *payload_size = 0; + + return 0; +} + +int mausb_send_isoch_out_msg(struct mausb_device *ma_dev, + struct mausb_event *mausb_event, + struct mausb_urb_ctx *urb_ctx) +{ + u32 starting_segments = 0; + u32 rem_transfer_buf = MAX_ISOCH_ASAP_PACKET_SIZE; + struct urb *urb = (struct urb *)mausb_event->data.urb; + u32 number_of_packets = (u32)urb->number_of_packets; + u32 payload_size = 0; + u32 chunk_size; + u32 seq_n = 0; + int status; + u32 i; + + for (i = 0; i < number_of_packets; ++i) { + if (i < number_of_packets - 1) + chunk_size = urb->iso_frame_desc[i + 1].offset - + urb->iso_frame_desc[i].offset; + else + chunk_size = + mausb_data_iterator_length(&urb_ctx->iterator) - + urb->iso_frame_desc[i].offset; + + if (chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE > + rem_transfer_buf) { + if (payload_size == 0) { + mausb_pr_warn("Fragmentation"); + } else { + status = __mausb_send_isoch_out_packet + (ma_dev, mausb_event, urb_ctx, + &seq_n, &starting_segments, + &rem_transfer_buf, + &payload_size, i); + if (status < 0) + return status; + i--; + continue; + } + } else { + rem_transfer_buf -= + chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE; + payload_size += chunk_size; + } + + if (i == number_of_packets - 1 || rem_transfer_buf == 0) { + status = __mausb_send_isoch_out_packet + (ma_dev, mausb_event, urb_ctx, &seq_n, + &starting_segments, &rem_transfer_buf, + &payload_size, i + 1); + if (status < 0) + return status; + } + } + return 0; +} + +int mausb_receive_isoch_out(struct mausb_event *event) +{ + struct urb *urb = (struct urb *)event->data.urb; + int status = 0; + u16 i; + + mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d", + event->data.transfer_size, event->data.rem_transfer_size, + event->status); + + for (i = 0; i < urb->number_of_packets; ++i) + urb->iso_frame_desc[i].status = event->status; + + mausb_complete_request(urb, event->data.payload_size, event->status); + + return status; +} diff --git a/drivers/usb/mausb_host/src/hpal/mausb_events.c b/drivers/usb/mausb_host/src/hpal/mausb_events.c new file mode 100644 index 000000000000..baf6bdf2f40c --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/mausb_events.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include + +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_data_iterator.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; + + mausb_pr_info("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 { + mausb_pr_err("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 = (u64)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 = (u64)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) +{ + mausb_pr_debug("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); + + kfree(hdr); + mausb_pr_warn("Unknown event type event=%d", hdr->type); + 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); + mausb_pr_err("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) { + mausb_pr_err("Usbdevhandle failed, event_id=%lld", event_id); + 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); +} + +static void mausb_populate_standard_ep_descriptor(struct usb_ep_desc *std_desc, + struct usb_endpoint_descriptor + *usb_std_desc) +{ + std_desc->bLength = usb_std_desc->bLength; + std_desc->bDescriptorType = usb_std_desc->bDescriptorType; + std_desc->bEndpointAddress = usb_std_desc->bEndpointAddress; + std_desc->bmAttributes = usb_std_desc->bmAttributes; + std_desc->wMaxPacketSize = usb_std_desc->wMaxPacketSize; + std_desc->bInterval = usb_std_desc->bInterval; +} + +static void +mausb_populate_superspeed_ep_descriptor(struct usb_ss_ep_comp_desc *ss_desc, + struct usb_ss_ep_comp_descriptor* + usb_ss_desc) +{ + ss_desc->bLength = usb_ss_desc->bLength; + ss_desc->bDescriptorType = usb_ss_desc->bDescriptorType; + ss_desc->bMaxBurst = usb_ss_desc->bMaxBurst; + ss_desc->bmAttributes = usb_ss_desc->bmAttributes; + ss_desc->wBytesPerInterval = usb_ss_desc->wBytesPerInterval; +} + +void +mausb_init_standard_ep_descriptor(struct ma_usb_ephandlereq_desc_std *std_desc, + struct usb_endpoint_descriptor *usb_std_desc) +{ + mausb_populate_standard_ep_descriptor(&std_desc->usb20, usb_std_desc); +} + +void +mausb_init_superspeed_ep_descriptor(struct ma_usb_ephandlereq_desc_ss *ss_desc, + struct usb_endpoint_descriptor * + usb_std_desc, + struct usb_ss_ep_comp_descriptor * + usb_ss_desc) +{ + mausb_populate_standard_ep_descriptor(&ss_desc->usb20, usb_std_desc); + mausb_populate_superspeed_ep_descriptor(&ss_desc->usb31, usb_ss_desc); +} + +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) { + mausb_pr_err("Ephandle failed, event_id=%lld", event_id); + 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) { + mausb_pr_err("Epactivate failed, event_id=%lld", event_id); + 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) { + mausb_pr_err("Epinactivate failed, event_id=%lld", event_id); + 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) { + mausb_pr_err("Epreset failed, event_id=%lld", event_id); + 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) { + mausb_pr_err("Epdelete failed, event_id=%lld", event_id); + 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) { + mausb_pr_err("Modifyep0 failed, event_id=%lld", event_id); + 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) { + mausb_pr_err("Setusbdevaddress failed, event_id=%lld", + event_id); + 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) { + mausb_pr_err("Updatedev failed, event_id=%lld", event_id); + 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) + mausb_pr_err("Ring buffer full, usbdevdisconnect failed"); + + 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) + mausb_pr_err("Ring buffer full, devdisconnect failed"); + + return status; +} + +__attribute__((unused)) +static int mausb_devdisconnect_event_to_user(struct mausb_device *dev) +{ + struct mausb_event event; + int status; + + event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT; + event.madev_addr = dev->madev_addr; + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) + mausb_pr_err("Ring buffer full, devdisconnect failed"); + + return status; +} + +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) { + mausb_pr_err("Usbdevreset failed, event_id=%lld", event_id); + 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, u64 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) { + mausb_pr_err("Ring buffer full, canceltransfer failed"); + return status; + } + + return status; +} + +int mausb_canceltransfer_event_from_user(struct mausb_device *dev, + struct mausb_event *event) +{ + mausb_pr_debug(""); + return 0; +} + +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) { + mausb_pr_warn("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); +} diff --git a/drivers/usb/mausb_host/src/hpal/network_callbacks.c b/drivers/usb/mausb_host/src/hpal/network_callbacks.c new file mode 100644 index 000000000000..b8bcbdf7e20f --- /dev/null +++ b/drivers/usb/mausb_host/src/hpal/network_callbacks.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "hpal/network_callbacks.h" + +#include +#include + +#include "hpal/hpal.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_logs.h" + +static void mausb_init_ip_ctx_helper(struct mausb_device *dev, + struct mausb_ip_ctx **ip_ctx, + u16 port, + enum mausb_channel channel) +{ + int status; + + status = mausb_init_ip_ctx(ip_ctx, dev->net_ns, + dev->dev_addr.ip.address.ip4, port, dev, + mausb_ip_callback, channel); + if (status < 0) { + mausb_pr_err("Init ip context failed with error=%d", status); + queue_work(dev->workq, &dev->socket_disconnect_work); + return; + } + + dev->channel_map[channel] = *ip_ctx; + mausb_ip_connect_async(*ip_ctx); +} + +static void mausb_connect_callback(struct mausb_device *dev, enum mausb_channel + channel, int status) +{ + struct mausb_device_address *dev_addr = &dev->dev_addr; + + mausb_pr_info("Connect callback for channel=%d with status=%d", + channel, status); + + if (status < 0) { + queue_work(dev->workq, &dev->socket_disconnect_work); + return; + } + + if (channel == MAUSB_MGMT_CHANNEL) { + if (dev_addr->ip.port.control == 0) { + dev->channel_map[MAUSB_CTRL_CHANNEL] = + dev->mgmt_channel; + channel = MAUSB_CTRL_CHANNEL; + } else { + mausb_init_ip_ctx_helper(dev, &dev->ctrl_channel, + dev_addr->ip.port.control, + MAUSB_CTRL_CHANNEL); + return; + } + } + + if (channel == MAUSB_CTRL_CHANNEL) { + if (dev_addr->ip.port.bulk == 0) { + dev->channel_map[MAUSB_BULK_CHANNEL] = + dev->channel_map[MAUSB_CTRL_CHANNEL]; + channel = MAUSB_BULK_CHANNEL; + } else { + mausb_init_ip_ctx_helper(dev, &dev->bulk_channel, + dev_addr->ip.port.bulk, + MAUSB_BULK_CHANNEL); + return; + } + } + + if (channel == MAUSB_BULK_CHANNEL) { + if (dev_addr->ip.port.isochronous == 0) { + /* if there is no isoch port use tcp for it */ + dev->channel_map[MAUSB_ISOCH_CHANNEL] = + dev->channel_map[MAUSB_BULK_CHANNEL]; + channel = MAUSB_ISOCH_CHANNEL; + } else { + mausb_init_ip_ctx_helper(dev, &dev->isoch_channel, + dev_addr->ip.port.isochronous, + MAUSB_ISOCH_CHANNEL); + return; + } + } + + if (channel == MAUSB_ISOCH_CHANNEL) { + dev->channel_map[MAUSB_INTR_CHANNEL] = + dev->channel_map[MAUSB_CTRL_CHANNEL]; + mausb_on_madev_connected(dev); + } +} + +static void mausb_handle_connect_event(struct mausb_device *dev, + enum mausb_channel channel, int status, + void *data) +{ + mausb_connect_callback(dev, channel, status); +} + +static void mausb_handle_receive_event(struct mausb_device *dev, + enum mausb_channel channel, int status, + void *data) +{ + struct mausb_event event; + + event.madev_addr = dev->madev_addr; + + if (status <= 0) { + mausb_pr_err("Receive event error status=%d", status); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + return; + } + + mausb_reset_connection_timer(dev); + + status = mausb_msg_received_event(&event, + (struct ma_usb_hdr_common *)data, + channel); + + if (status == 0) + status = mausb_enqueue_event_to_user(dev, &event); + + if (status < 0) + mausb_pr_err("Failed to enqueue, status=%d", status); +} + +void mausb_ip_callback(void *ctx, enum mausb_channel channel, + enum mausb_link_action action, int status, void *data) +{ + struct mausb_device *dev = (struct mausb_device *)ctx; + + switch (action) { + case MAUSB_LINK_CONNECT: + mausb_handle_connect_event(dev, channel, status, data); + break; + case MAUSB_LINK_SEND: + /* + * Currently there is nothing to do, as send operation is + * synchronous + */ + break; + case MAUSB_LINK_RECV: + mausb_handle_receive_event(dev, channel, status, data); + break; + case MAUSB_LINK_DISCONNECT: + /* + * Currently there is nothing to do, as disconnect operation is + * synchronous + */ + break; + default: + mausb_pr_warn("Unknown network action"); + } +} diff --git a/drivers/usb/mausb_host/src/link/mausb_ip_link.c b/drivers/usb/mausb_host/src/link/mausb_ip_link.c new file mode 100644 index 000000000000..bcd6d00d12cd --- /dev/null +++ b/drivers/usb/mausb_host/src/link/mausb_ip_link.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "link/mausb_ip_link.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/mausb_logs.h" + +static void __mausb_ip_connect(struct work_struct *work); +static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx); +static void __mausb_ip_recv_work(struct work_struct *work); +static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx + *recv_ctx); +static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx + *recv_ctx); + +int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx, + struct net *net_ns, + char ip_addr[INET6_ADDRSTRLEN], + u16 port, void *context, + void (*fn_callback)(void *ctx, enum mausb_channel channel, + enum mausb_link_action act, + int status, void *data), + enum mausb_channel channel) +{ + struct mausb_ip_ctx *ctx; + *ip_ctx = kzalloc(sizeof(**ip_ctx), GFP_ATOMIC); + if (!*ip_ctx) { + mausb_pr_alert("ip context allocation failed"); + return -ENOMEM; + } + ctx = *ip_ctx; + ctx->client_socket = NULL; + __mausb_ip_recv_ctx_clear(&ctx->recv_ctx); + strcpy(ctx->ip_addr, ip_addr); + ctx->port = port; + ctx->net_ns = net_ns; + + if (channel == MAUSB_ISOCH_CHANNEL) + ctx->udp = true; + + ctx->connect_workq = alloc_ordered_workqueue("connect_workq", + WQ_MEM_RECLAIM); + if (!ctx->connect_workq) { + mausb_pr_alert("connect_workq alloc failed"); + kfree(ctx); + return -ENOMEM; + } + + ctx->recv_workq = + alloc_ordered_workqueue("recv_workq", WQ_MEM_RECLAIM); + if (!ctx->recv_workq) { + mausb_pr_alert("send_recv_workq alloc failed"); + destroy_workqueue(ctx->connect_workq); + kfree(ctx); + return -ENOMEM; + } + + INIT_WORK(&ctx->connect_work, __mausb_ip_connect); + INIT_WORK(&ctx->recv_work, __mausb_ip_recv_work); + + ctx->channel = channel; + ctx->ctx = context; + ctx->fn_callback = fn_callback; + + return 0; +} + +void mausb_destroy_ip_ctx(struct mausb_ip_ctx *ip_ctx) +{ + if (!ip_ctx) + return; + + if (ip_ctx->connect_workq) { + flush_workqueue(ip_ctx->connect_workq); + destroy_workqueue(ip_ctx->connect_workq); + } + + if (ip_ctx->recv_workq) { + flush_workqueue(ip_ctx->recv_workq); + destroy_workqueue(ip_ctx->recv_workq); + } + if (ip_ctx->client_socket) + sock_release(ip_ctx->client_socket); + __mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx); + + kfree(ip_ctx); +} + +static void __mausb_ip_set_options(struct socket *sock, bool udp) +{ + u32 optval = 0; + unsigned int optlen = sizeof(optval); + int status = 0; + struct timeval timeo = {.tv_sec = 0, .tv_usec = 500000U }; + struct timeval send_timeo = {.tv_sec = 1, .tv_usec = 0 }; + + if (!udp) { + optval = 1; + status = kernel_setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&optval, optlen); + if (status < 0) + mausb_pr_warn("Failed to set tcp no delay option: status=%d", + status); + } + + status = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_NEW, + (char *)&timeo, sizeof(timeo)); + if (status < 0) + mausb_pr_warn("Failed to set recv timeout option: status=%d", + status); + + status = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO_NEW, + (char *)&send_timeo, sizeof(send_timeo)); + if (status < 0) + mausb_pr_warn("Failed to set snd timeout option: status=%d", + status); + + optval = MAUSB_LINK_BUFF_SIZE; + status = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + (char *)&optval, optlen); + if (status < 0) + mausb_pr_warn("Failed to set recv buffer size: status=%d", + status); + + optval = MAUSB_LINK_BUFF_SIZE; + status = kernel_setsockopt(sock, SOL_SOCKET, SO_SNDBUF, + (char *)&optval, optlen); + if (status < 0) + mausb_pr_warn("Failed to set send buffer size: status=%d", + status); + + optval = MAUSB_LINK_TOS_LEVEL_EF; + status = kernel_setsockopt(sock, IPPROTO_IP, IP_TOS, + (char *)&optval, optlen); + if (status < 0) + mausb_pr_warn("Failed to set QOS: status=%d", status); +} + +static void __mausb_ip_connect(struct work_struct *work) +{ + struct sockaddr_in sockaddr; + int status = 0; + + struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx, + connect_work); + + if (!ip_ctx->udp) { + status = sock_create_kern(ip_ctx->net_ns, AF_INET, SOCK_STREAM, + IPPROTO_TCP, &ip_ctx->client_socket); + if (status < 0) { + mausb_pr_err("Failed to create socket: status=%d", + status); + goto callback; + } + } else { + status = sock_create_kern(ip_ctx->net_ns, AF_INET, SOCK_DGRAM, + IPPROTO_UDP, &ip_ctx->client_socket); + if (status < 0) { + mausb_pr_err("Failed to create socket: status=%d", + status); + goto callback; + } + } + + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_port = htons(ip_ctx->port); + sockaddr.sin_addr.s_addr = in_aton(ip_ctx->ip_addr); + + __mausb_ip_set_options((struct socket *)ip_ctx->client_socket, + ip_ctx->udp); + + status = kernel_connect(ip_ctx->client_socket, + (struct sockaddr *)&sockaddr, sizeof(sockaddr), + O_RDWR); + if (status < 0) { + mausb_pr_err("Failed to connect to host %s:%d, status=%d", + ip_ctx->ip_addr, ip_ctx->port, status); + goto clear_socket; + } + + queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work); + mausb_pr_info("Connected to host %s:%d, status=%d", ip_ctx->ip_addr, + ip_ctx->port, status); + + goto callback; + +clear_socket: + sock_release(ip_ctx->client_socket); + ip_ctx->client_socket = NULL; +callback: + ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel, MAUSB_LINK_CONNECT, + status, NULL); +} + +void mausb_ip_connect_async(struct mausb_ip_ctx *ip_ctx) +{ + queue_work(ip_ctx->connect_workq, &ip_ctx->connect_work); +} + +int mausb_ip_disconnect(struct mausb_ip_ctx *ip_ctx) +{ + if (ip_ctx && ip_ctx->client_socket) + return kernel_sock_shutdown(ip_ctx->client_socket, SHUT_RDWR); + return 0; +} + +int mausb_ip_send(struct mausb_ip_ctx *ip_ctx, + struct mausb_kvec_data_wrapper *wrapper) +{ + struct msghdr msghd; + + if (!ip_ctx) { + mausb_pr_alert("Socket ctx is NULL!"); + return -EINVAL; + } + + memset(&msghd, 0, sizeof(msghd)); + msghd.msg_flags = MSG_WAITALL; + + return kernel_sendmsg(ip_ctx->client_socket, &msghd, wrapper->kvec, + wrapper->kvec_num, wrapper->length); +} + +static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx *recv_ctx) +{ + recv_ctx->buffer = NULL; + recv_ctx->left = 0; + recv_ctx->received = 0; +} + +static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx *recv_ctx) +{ + kfree(recv_ctx->buffer); + __mausb_ip_recv_ctx_clear(recv_ctx); +} + +static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx) +{ + struct msghdr msghd; + struct kvec vec; + int status; + bool peek = true; + unsigned int optval = 1; + struct socket *client_socket = (struct socket *)ip_ctx->client_socket; + + /* receive with timeout of 0.5s */ + while (true) { + memset(&msghd, 0, sizeof(msghd)); + if (peek) { + vec.iov_base = ip_ctx->recv_ctx.common_hdr; + vec.iov_len = sizeof(ip_ctx->recv_ctx.common_hdr); + msghd.msg_flags = MSG_PEEK; + } else { + vec.iov_base = + ip_ctx->recv_ctx.buffer + + ip_ctx->recv_ctx.received; + vec.iov_len = ip_ctx->recv_ctx.left; + msghd.msg_flags = MSG_WAITALL; + } + + if (!ip_ctx->udp) { + status = kernel_setsockopt(client_socket, IPPROTO_TCP, + TCP_QUICKACK, + (char *)&optval, + sizeof(optval)); + if (status != 0) { + mausb_pr_warn("Setting TCP_QUICKACK failed: %s:%d, status=%d", + ip_ctx->ip_addr, ip_ctx->port, + status); + } + } + + status = kernel_recvmsg(client_socket, &msghd, &vec, 1, + vec.iov_len, (int)msghd.msg_flags); + if (status == -EAGAIN) { + return -EAGAIN; + } else if (status <= 0) { + mausb_pr_warn("kernel_recvmsg host %s:%d, status=%d", + ip_ctx->ip_addr, ip_ctx->port, status); + + __mausb_ip_recv_ctx_free(&ip_ctx->recv_ctx); + ip_ctx->fn_callback(ip_ctx->ctx, ip_ctx->channel, + MAUSB_LINK_RECV, status, NULL); + return status; + } + + mausb_pr_debug("kernel_recvmsg host %s:%d, status=%d", + ip_ctx->ip_addr, ip_ctx->port, status); + + if (peek) { + if ((unsigned int)status < + sizeof(ip_ctx->recv_ctx.common_hdr)) + return -EAGAIN; + /* length field of mausb_common_hdr */ + ip_ctx->recv_ctx.left = + *(u16 *)(&ip_ctx->recv_ctx.common_hdr[2]); + ip_ctx->recv_ctx.received = 0; + ip_ctx->recv_ctx.buffer = + kzalloc(ip_ctx->recv_ctx.left, GFP_KERNEL); + peek = false; + if (!ip_ctx->recv_ctx.buffer) { + ip_ctx->fn_callback(ip_ctx->ctx, + ip_ctx->channel, + MAUSB_LINK_RECV, + -ENOMEM, NULL); + return -ENOMEM; + } + } else { + if (status < ip_ctx->recv_ctx.left) { + ip_ctx->recv_ctx.left -= (u16)status; + ip_ctx->recv_ctx.received += (u16)status; + } else { + ip_ctx->fn_callback(ip_ctx->ctx, + ip_ctx->channel, + MAUSB_LINK_RECV, status, + ip_ctx->recv_ctx.buffer); + __mausb_ip_recv_ctx_clear(&ip_ctx->recv_ctx); + peek = true; + } + } + } + + return status; +} + +static void __mausb_ip_recv_work(struct work_struct *work) +{ + struct mausb_ip_ctx *ip_ctx = container_of(work, struct mausb_ip_ctx, + recv_work); + int status = __mausb_ip_recv(ip_ctx); + + if (status <= 0 && status != -EAGAIN) + return; + + queue_work(ip_ctx->recv_workq, &ip_ctx->recv_work); +} diff --git a/drivers/usb/mausb_host/src/utils/mausb_data_iterator.c b/drivers/usb/mausb_host/src/utils/mausb_data_iterator.c new file mode 100644 index 000000000000..0ea7d7190bda --- /dev/null +++ b/drivers/usb/mausb_host/src/utils/mausb_data_iterator.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "utils/mausb_data_iterator.h" + +#include +#include +#include + +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" + +static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator, + u32 byte_num, + struct list_head *data_chunks_list, + u32 *data_chunks_num) +{ + u32 rem_data = 0; + u32 bytes_to_read = 0; + struct mausb_payload_chunk *data_chunk = NULL; + + (*data_chunks_num) = 0; + + if (!data_chunks_list) + return -EINVAL; + + INIT_LIST_HEAD(data_chunks_list); + rem_data = iterator->length - iterator->offset; + bytes_to_read = min(byte_num, rem_data); + + if (bytes_to_read == 0) + return 0; + + data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL); + + if (!data_chunk) + return -ENOMEM; + + ++(*data_chunks_num); + + data_chunk->kvec.iov_base = (u8 *)(iterator->buffer) + iterator->offset; + data_chunk->kvec.iov_len = bytes_to_read; + iterator->offset += bytes_to_read; + + list_add_tail(&data_chunk->list_entry, data_chunks_list); + + return 0; +} + +static int mausb_read_scatterlist_buffer(struct mausb_data_iter *iterator, + u32 byte_num, + struct list_head *data_chunks_list, + u32 *data_chunks_num) +{ + u32 current_sg_read_num; + struct mausb_payload_chunk *data_chunk = NULL; + + (*data_chunks_num) = 0; + + if (!data_chunks_list) + return -EINVAL; + + INIT_LIST_HEAD(data_chunks_list); + + while (byte_num) { + if (iterator->sg_iter.consumed == iterator->sg_iter.length) { + if (!sg_miter_next(&iterator->sg_iter)) + break; + iterator->sg_iter.consumed = 0; + } + + data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL); + if (!data_chunk) { + sg_miter_stop(&iterator->sg_iter); + return -ENOMEM; + } + + current_sg_read_num = min((size_t)byte_num, + iterator->sg_iter.length - + iterator->sg_iter.consumed); + + data_chunk->kvec.iov_base = (u8 *)iterator->sg_iter.addr + + iterator->sg_iter.consumed; + data_chunk->kvec.iov_len = current_sg_read_num; + + ++(*data_chunks_num); + list_add_tail(&data_chunk->list_entry, data_chunks_list); + + byte_num -= current_sg_read_num; + iterator->sg_iter.consumed += current_sg_read_num; + data_chunk = NULL; + } + + return 0; +} + +static u32 mausb_write_virtual_buffer(struct mausb_data_iter *iterator, + void *buffer, u32 size) +{ + u32 rem_space = 0; + u32 write_count = 0; + + if (!buffer || !size) + return write_count; + + rem_space = iterator->length - iterator->offset; + write_count = min(size, rem_space); + + if (write_count > 0) { + void *location = shift_ptr(iterator->buffer, iterator->offset); + + memcpy(location, buffer, write_count); + iterator->offset += write_count; + } + + return write_count; +} + +static u32 mausb_write_scatterlist_buffer(struct mausb_data_iter *iterator, + void *buffer, u32 size) +{ + u32 current_sg_rem_space; + u32 count = 0; + u32 total_count = 0; + void *location = NULL; + + if (!buffer || !size) + return count; + + while (size) { + if (iterator->sg_iter.consumed >= iterator->sg_iter.length) { + if (!sg_miter_next(&iterator->sg_iter)) + break; + iterator->sg_iter.consumed = 0; + } + + current_sg_rem_space = (u32)(iterator->sg_iter.length - + iterator->sg_iter.consumed); + + count = min(size, current_sg_rem_space); + total_count += count; + + location = shift_ptr(iterator->sg_iter.addr, + iterator->sg_iter.consumed); + + memcpy(location, buffer, count); + + buffer = shift_ptr(buffer, count); + size -= count; + iterator->sg_iter.consumed += count; + } + + return total_count; +} + +int mausb_data_iterator_read(struct mausb_data_iter *iterator, + u32 byte_num, + struct list_head *data_chunks_list, + u32 *data_chunks_num) +{ + if (iterator->buffer) + return mausb_read_virtual_buffer(iterator, byte_num, + data_chunks_list, + data_chunks_num); + else + return mausb_read_scatterlist_buffer(iterator, byte_num, + data_chunks_list, + data_chunks_num); +} + +u32 mausb_data_iterator_write(struct mausb_data_iter *iterator, void *buffer, + u32 length) +{ + if (iterator->buffer) + return mausb_write_virtual_buffer(iterator, buffer, length); + else + return mausb_write_scatterlist_buffer(iterator, buffer, length); +} + +static inline void mausb_seek_virtual_buffer(struct mausb_data_iter *iterator, + u32 seek_delta) +{ + iterator->offset += min(seek_delta, iterator->length - + iterator->offset); +} + +static void mausb_seek_scatterlist_buffer(struct mausb_data_iter *iterator, + u32 seek_delta) +{ + u32 rem_bytes_in_current_scatter; + + while (seek_delta) { + rem_bytes_in_current_scatter = (u32)(iterator->sg_iter.length - + iterator->sg_iter.consumed); + if (rem_bytes_in_current_scatter <= seek_delta) { + iterator->sg_iter.consumed += + rem_bytes_in_current_scatter; + seek_delta -= rem_bytes_in_current_scatter; + if (!sg_miter_next(&iterator->sg_iter)) + break; + iterator->sg_iter.consumed = 0; + } else { + iterator->sg_iter.consumed += seek_delta; + break; + } + } +} + +void mausb_data_iterator_seek(struct mausb_data_iter *iterator, + u32 seek_delta) +{ + if (iterator->buffer) + mausb_seek_virtual_buffer(iterator, seek_delta); + else + mausb_seek_scatterlist_buffer(iterator, seek_delta); +} + +static void mausb_calculate_buffer_length(struct mausb_data_iter *iterator) +{ + /* Calculate buffer length */ + if (iterator->buffer_len > 0) { + /* Transfer_buffer_length is populated */ + iterator->length = iterator->buffer_len; + } else if (iterator->sg && iterator->num_sgs != 0) { + /* Transfer_buffer_length is not populated */ + sg_miter_start(&iterator->sg_iter, iterator->sg, + iterator->num_sgs, iterator->flags); + while (sg_miter_next(&iterator->sg_iter)) + iterator->length += (u32)iterator->sg_iter.length; + sg_miter_stop(&iterator->sg_iter); + } else { + iterator->length = 0; + } +} + +void mausb_init_data_iterator(struct mausb_data_iter *iterator, void *buffer, + u32 buffer_len, struct scatterlist *sg, + unsigned int num_sgs, bool direction) +{ + iterator->offset = 0; + iterator->buffer = buffer; + iterator->buffer_len = buffer_len; + iterator->length = 0; + iterator->sg = sg; + iterator->num_sgs = num_sgs; + iterator->sg_started = 0; + + mausb_calculate_buffer_length(iterator); + + if (!buffer && sg && num_sgs != 0) { + /* Scatterlist provided */ + iterator->flags = direction ? SG_MITER_TO_SG : SG_MITER_FROM_SG; + sg_miter_start(&iterator->sg_iter, sg, num_sgs, + iterator->flags); + iterator->sg_started = 1; + } +} + +void mausb_uninit_data_iterator(struct mausb_data_iter *iterator) +{ + iterator->offset = 0; + iterator->length = 0; + iterator->buffer = NULL; + iterator->buffer_len = 0; + + if (iterator->sg_started) + sg_miter_stop(&iterator->sg_iter); + + iterator->sg_started = 0; +} + +void mausb_reset_data_iterator(struct mausb_data_iter *iterator) +{ + iterator->offset = 0; + if (iterator->sg_started) { + sg_miter_stop(&iterator->sg_iter); + iterator->sg_started = 0; + } + + if (!iterator->buffer && iterator->sg && iterator->num_sgs != 0) { + sg_miter_start(&iterator->sg_iter, iterator->sg, + iterator->num_sgs, iterator->flags); + iterator->sg_started = 1; + } +} + +u32 mausb_data_iterator_length(struct mausb_data_iter *iterator) +{ + return iterator->length; +} diff --git a/drivers/usb/mausb_host/src/utils/mausb_mmap.c b/drivers/usb/mausb_host/src/utils/mausb_mmap.c new file mode 100644 index 000000000000..8ab073d7d662 --- /dev/null +++ b/drivers/usb/mausb_host/src/utils/mausb_mmap.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "utils/mausb_mmap.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/mausb_driver_status.h" +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_ring_buffer.h" + +#define MAUSB_KERNEL_DEV_NAME "mausb_host" +#define MAUSB_READ_DEVICE_TIMEOUT_MS 500 + +static dev_t mausb_major_kernel; +static struct cdev mausb_kernel_dev; +static struct class *mausb_kernel_class; + +static void mausb_vm_open(struct vm_area_struct *vma) +{ + mausb_pr_debug(""); +} + +static void mausb_vm_close(struct vm_area_struct *vma) +{ + struct mausb_ring_buffer *buffer = NULL, *next = NULL; + unsigned long flags = 0; + u64 ring_buffer_id = *(u64 *)(vma->vm_private_data); + + mausb_pr_info("Releasing ring buffer with id: %llu", ring_buffer_id); + spin_lock_irqsave(&mss.lock, flags); + list_for_each_entry_safe(buffer, next, &mss.available_ring_buffers, + list_entry) { + if (buffer->id == ring_buffer_id) { + list_del(&buffer->list_entry); + mausb_ring_buffer_destroy(buffer); + kfree(buffer); + break; + } + } + spin_unlock_irqrestore(&mss.lock, flags); +} + +/* + * mausb_vm_fault is called the first time a memory area is accessed which is + * not in memory + */ +static vm_fault_t mausb_vm_fault(struct vm_fault *vmf) +{ + mausb_pr_debug(""); + return 0; +} + +static const struct vm_operations_struct mausb_vm_ops = { + .open = mausb_vm_open, + .close = mausb_vm_close, + .fault = mausb_vm_fault, +}; + +static int mausb_file_open(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + + return 0; +} + +static int mausb_file_close(struct inode *inode, struct file *filp) +{ + kfree(filp->private_data); + filp->private_data = NULL; + + return 0; +} + +static ssize_t mausb_file_read(struct file *filp, char __user *user_buffer, + size_t size, loff_t *offset) +{ + ssize_t num_of_bytes_to_read = MAUSB_MAX_NUM_OF_MA_DEVS * + sizeof(struct mausb_events_notification); + unsigned long num_of_bytes_not_copied; + int completed_events; + int ring_events; + struct mausb_ring_buffer *ring_buffer; + struct mausb_device *dev; + struct completion *ring_has_events; + u8 current_device = 0; + s8 fail_ret_val; + unsigned long flags; + unsigned long timeout; + long status; + + /* Reset heartbeat timer events */ + mausb_reset_heartbeat_cnt(); + + if ((ssize_t)size != num_of_bytes_to_read) { + mausb_pr_alert("Different expected bytes to read (%ld) from actual size (%ld)", + num_of_bytes_to_read, size); + fail_ret_val = MAUSB_DRIVER_BAD_READ_BUFFER_SIZE; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + /* If suspend/hibernate happened delete all devices */ + if (atomic_xchg(&mss.num_of_transitions_to_sleep, 0)) { + mausb_pr_alert("Suspend system event detected"); + fail_ret_val = MAUSB_DRIVER_SYSTEM_SUSPENDED; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + ring_has_events = &mss.rings_events.mausb_ring_has_events; + timeout = msecs_to_jiffies(MAUSB_READ_DEVICE_TIMEOUT_MS); + status = wait_for_completion_interruptible_timeout(ring_has_events, + timeout); + reinit_completion(ring_has_events); + + if (atomic_read(&mss.rings_events.mausb_stop_reading_ring_events)) { + mausb_pr_alert("Ring events stopped"); + fail_ret_val = MAUSB_DRIVER_RING_EVENTS_STOPPED; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + /* There are no new events - waiting for events hit timeout */ + if (status == 0) + return MAUSB_DRIVER_READ_TIMEOUT; + + spin_lock_irqsave(&mss.lock, flags); + + list_for_each_entry(dev, &mss.madev_list, list_entry) { + mss.events[current_device].madev_addr = dev->madev_addr; + ring_buffer = dev->ring_buffer; + ring_events = atomic_xchg(&ring_buffer->mausb_ring_events, 0); + completed_events = + atomic_xchg(&ring_buffer->mausb_completed_user_events, + 0); + mss.events[current_device].num_of_events = (u16)ring_events; + mss.events[current_device].num_of_completed_events = + (u16)completed_events; + if (++current_device == MAUSB_MAX_NUM_OF_MA_DEVS) + break; + } + + spin_unlock_irqrestore(&mss.lock, flags); + + num_of_bytes_to_read = + (ssize_t)(current_device * + sizeof(struct mausb_events_notification)); + num_of_bytes_not_copied = + copy_to_user(user_buffer, &mss.events, + (unsigned long)num_of_bytes_to_read); + + mausb_pr_debug("num_of_bytes_not_copied %ld, num_of_bytes_to_read %ld", + num_of_bytes_not_copied, num_of_bytes_to_read); + + if (num_of_bytes_not_copied) { + fail_ret_val = MAUSB_DRIVER_COPY_TO_BUFFER_FAILED; + if (copy_to_user(user_buffer, &fail_ret_val, + sizeof(fail_ret_val)) != 0) { + mausb_pr_warn("Failed to set error code."); + } + return MAUSB_DRIVER_READ_ERROR; + } + + return num_of_bytes_to_read; +} + +static ssize_t mausb_file_write(struct file *filp, const char __user *buffer, + size_t size, loff_t *offset) +{ + ssize_t num_of_bytes_to_write = + sizeof(struct mausb_events_notification); + struct mausb_events_notification notification; + unsigned long flags; + struct mausb_device *dev; + + if (size != (size_t)num_of_bytes_to_write) { + mausb_pr_alert("Different expected bytes to write (%ld) from actual size (%ld)", + num_of_bytes_to_write, size); + return MAUSB_DRIVER_WRITE_ERROR; + } + + copy_from_user(¬ification, buffer, size); + + spin_lock_irqsave(&mss.lock, flags); + dev = mausb_get_dev_from_addr_unsafe(notification.madev_addr); + + if (!dev) { + spin_unlock_irqrestore(&mss.lock, flags); + return 0; + } + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + dev->num_of_user_events += notification.num_of_events; + dev->num_of_completed_events += notification.num_of_completed_events; + spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags); + + queue_work(dev->workq, &dev->work); + spin_unlock_irqrestore(&mss.lock, flags); + + return num_of_bytes_to_write; +} + +static inline unsigned long mausb_ring_buffer_length(void) +{ + int page_order = mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE, + sizeof(struct mausb_event)); + return PAGE_SIZE << page_order; +} + +static int mausb_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long size = vma->vm_end - vma->vm_start; + int ret; + struct page *page = NULL; + unsigned long flags = 0; + struct mausb_ring_buffer *ring_buffer = kzalloc(sizeof(*ring_buffer), + GFP_KERNEL); + if (!ring_buffer) + return -ENOMEM; + + ret = mausb_ring_buffer_init(ring_buffer); + if (ret < 0) { + mausb_pr_err("Ring buffer init failed"); + goto release_ring_buffer; + } + + vma->vm_private_data = kzalloc(sizeof(ring_buffer->id), GFP_KERNEL); + if (!vma->vm_private_data) { + ret = -ENOMEM; + goto release_ring_buffer; + } + + filp->private_data = vma->vm_private_data; + + if (size > mausb_ring_buffer_length()) { + mausb_pr_err("Invalid memory size to map"); + ret = -EINVAL; + goto release_ring_buffer; + } + + vma->vm_ops = &mausb_vm_ops; + mausb_vm_open(vma); + + page = virt_to_page(ring_buffer->to_user_buffer); + ret = remap_pfn_range(vma, vma->vm_start, page_to_pfn(page), size, + vma->vm_page_prot); + if (ret < 0) { + mausb_pr_err("Could not map the address area"); + goto release_ring_buffer; + } + + spin_lock_irqsave(&mss.lock, flags); + ring_buffer->id = mss.ring_buffer_id++; + *(u64 *)(vma->vm_private_data) = ring_buffer->id; + list_add_tail(&ring_buffer->list_entry, &mss.available_ring_buffers); + mausb_pr_info("Allocated ring buffer with id: %llu", ring_buffer->id); + spin_unlock_irqrestore(&mss.lock, flags); + + return 0; + +release_ring_buffer: + mausb_ring_buffer_destroy(ring_buffer); + kfree(ring_buffer); + return ret; +} + +static char *mausb_kernel_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + *mode = 0666; + return NULL; +} + +static const struct file_operations mausb_file_ops = { + .open = mausb_file_open, + .release = mausb_file_close, + .read = mausb_file_read, + .write = mausb_file_write, + .mmap = mausb_mmap, +}; + +int mausb_create_dev(void) +{ + int device_created = 0; + int status = alloc_chrdev_region(&mausb_major_kernel, 0, 1, + MAUSB_KERNEL_DEV_NAME "_proc"); + if (status) + goto cleanup; + + mausb_kernel_class = class_create(THIS_MODULE, + MAUSB_KERNEL_DEV_NAME "_sys"); + if (!mausb_kernel_class) { + status = -ENOMEM; + goto cleanup; + } + + mausb_kernel_class->devnode = mausb_kernel_devnode; + + if (!device_create(mausb_kernel_class, NULL, mausb_major_kernel, NULL, + MAUSB_KERNEL_DEV_NAME "_dev")) { + status = -ENOMEM; + goto cleanup; + } + device_created = 1; + cdev_init(&mausb_kernel_dev, &mausb_file_ops); + status = cdev_add(&mausb_kernel_dev, mausb_major_kernel, 1); + if (status) + goto cleanup; + return 0; +cleanup: + mausb_cleanup_dev(device_created); + return status; +} + +void mausb_cleanup_dev(int device_created) +{ + if (device_created) { + device_destroy(mausb_kernel_class, mausb_major_kernel); + mausb_pr_info("device destroyed"); + cdev_del(&mausb_kernel_dev); + mausb_pr_info("device deleted"); + } + if (mausb_kernel_class) { + class_destroy(mausb_kernel_class); + mausb_pr_info("class destroyed"); + } + + unregister_chrdev_region(mausb_major_kernel, 1); + mausb_pr_info("unregistered"); +} + +void mausb_notify_completed_user_events(struct mausb_ring_buffer *ring_buffer) +{ + int completed; + + completed = + atomic_inc_return(&ring_buffer->mausb_completed_user_events); + mausb_pr_debug("mausb_completed_user_events INCREMENTED %d", completed); + if (completed > MAUSB_RING_BUFFER_SIZE / 16) + complete(&mss.rings_events.mausb_ring_has_events); +} + +void mausb_notify_ring_events(struct mausb_ring_buffer *ring_buffer) +{ + int events; + + events = atomic_inc_return(&ring_buffer->mausb_ring_events); + if (events == 1) + complete(&mss.rings_events.mausb_ring_has_events); +} + +void mausb_stop_ring_events(void) +{ + atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 1); + complete(&mss.rings_events.mausb_ring_has_events); +} diff --git a/drivers/usb/mausb_host/src/utils/mausb_ring_buffer.c b/drivers/usb/mausb_host/src/utils/mausb_ring_buffer.c new file mode 100644 index 000000000000..2f3052304215 --- /dev/null +++ b/drivers/usb/mausb_host/src/utils/mausb_ring_buffer.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd. + * + * This file is subject to the terms and conditions of the GNU General Public + * License v2. See the file COPYING in the main directory of this archive for + * more details. + */ +#include "utils/mausb_ring_buffer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hpal/mausb_events.h" +#include "utils/mausb_logs.h" + +static int mausb_ring_buffer_get(struct mausb_ring_buffer *ring, + struct mausb_event *event) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ring->lock, flags); + if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 1) { + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + memcpy(event, ring->to_user_buffer + ring->tail, sizeof(*event)); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + ring->tail = (ring->tail + 1) & (MAUSB_RING_BUFFER_SIZE - 1); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +int mausb_ring_buffer_init(struct mausb_ring_buffer *ring) +{ + unsigned int page_order = + mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE, + sizeof(struct mausb_event)); + ring->to_user_buffer = + (struct mausb_event *)__get_free_pages(GFP_KERNEL, page_order); + if (!ring->to_user_buffer) + return -ENOMEM; + ring->from_user_buffer = ring->to_user_buffer + MAUSB_RING_BUFFER_SIZE; + ring->head = 0; + ring->tail = 0; + ring->current_from_user = 0; + ring->buffer_full = false; + spin_lock_init(&ring->lock); + + return 0; +} + +int mausb_ring_buffer_put(struct mausb_ring_buffer *ring, + struct mausb_event *event) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ring->lock, flags); + + if (ring->buffer_full) { + mausb_pr_err("Ring buffer is full"); + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + + if (CIRC_SPACE(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < 2) { + mausb_pr_err("Ring buffer capacity exceeded, disconnecting device"); + ring->buffer_full = true; + mausb_disconect_event_unsafe(ring, event->madev_addr); + ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1); + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + memcpy(ring->to_user_buffer + ring->head, event, sizeof(*event)); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + ring->head = (ring->head + 1) & (MAUSB_RING_BUFFER_SIZE - 1); + mausb_pr_debug("HEAD=%d, TAIL=%d", ring->head, ring->tail); + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, u32 count) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ring->lock, flags); + if (CIRC_CNT(ring->head, ring->tail, MAUSB_RING_BUFFER_SIZE) < count) { + spin_unlock_irqrestore(&ring->lock, flags); + return -ENOSPC; + } + mausb_pr_debug("old HEAD=%d, TAIL=%d", ring->head, ring->tail); + ring->tail = (ring->tail + (int)count) & (MAUSB_RING_BUFFER_SIZE - 1); + mausb_pr_debug("new HEAD=%d, TAIL=%d", ring->head, ring->tail); + spin_unlock_irqrestore(&ring->lock, flags); + return 0; +} + +void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring) +{ + struct mausb_event event; + + while (mausb_ring_buffer_get(ring, &event) == 0) + mausb_cleanup_ring_buffer_event(&event); +} + +void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring) +{ + unsigned int page_order = + mausb_get_page_order(2 * MAUSB_RING_BUFFER_SIZE, + sizeof(struct mausb_event)); + if (ring && ring->to_user_buffer) + free_pages((unsigned long)ring->to_user_buffer, page_order); +} + +void mausb_cleanup_ring_buffer_event(struct mausb_event *event) +{ + mausb_pr_debug("event=%d", event->type); + switch (event->type) { + case MAUSB_EVENT_TYPE_SEND_DATA_MSG: + mausb_cleanup_send_data_msg_event(event); + break; + case MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG: + mausb_cleanup_received_data_msg_event(event); + break; + case MAUSB_EVENT_TYPE_DELETE_DATA_TRANSFER: + mausb_cleanup_delete_data_transfer_event(event); + break; + case MAUSB_EVENT_TYPE_NONE: + break; + default: + mausb_pr_warn("Unknown event type"); + break; + } +} + +void mausb_disconect_event_unsafe(struct mausb_ring_buffer *ring, + uint8_t madev_addr) +{ + struct mausb_event disconnect_event; + struct mausb_device *dev = mausb_get_dev_from_addr_unsafe(madev_addr); + + if (!dev) { + mausb_pr_err("Device not found, madev_addr=%#x", madev_addr); + return; + } + + disconnect_event.type = MAUSB_EVENT_TYPE_DEV_DISCONNECT; + disconnect_event.status = -EINVAL; + disconnect_event.madev_addr = madev_addr; + + memcpy(ring->to_user_buffer + ring->head, &disconnect_event, + sizeof(disconnect_event)); + + mausb_pr_info("Disconnect event added to ring buffer for madev_addr=%#x", + madev_addr); + mausb_pr_info("Adding hcd_disconnect_work to dev workq, madev_addr=%#x", + madev_addr); + queue_work(dev->workq, &dev->hcd_disconnect_work); +} -- 2.17.1