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 DislayLink 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 <vladimir.stankovic@xxxxxxxxxxxxxxx> --- drivers/staging/Kconfig | 3 + drivers/staging/Makefile | 1 + drivers/staging/mausb_host/Kconfig | 15 + drivers/staging/mausb_host/Makefile | 30 + .../mausb_host/include/common/ma_usb.h | 851 ++++++++ .../mausb_host/include/common/mausb_address.h | 38 + .../include/common/mausb_driver_status.h | 20 + .../mausb_host/include/common/mausb_event.h | 228 +++ drivers/staging/mausb_host/include/hcd/hub.h | 120 ++ drivers/staging/mausb_host/include/hcd/vhcd.h | 81 + .../mausb_host/include/hpal/data_common.h | 67 + .../staging/mausb_host/include/hpal/data_in.h | 18 + .../mausb_host/include/hpal/data_out.h | 22 + .../staging/mausb_host/include/hpal/hpal.h | 174 ++ .../mausb_host/include/hpal/isoch_in.h | 22 + .../mausb_host/include/hpal/isoch_out.h | 21 + .../mausb_host/include/hpal/mausb_events.h | 97 + .../include/hpal/network_callbacks.h | 20 + .../mausb_host/include/link/mausb_ip_link.h | 89 + .../include/utils/mausb_data_iterator.h | 55 + .../mausb_host/include/utils/mausb_logs.h | 35 + .../mausb_host/include/utils/mausb_mmap.h | 22 + .../include/utils/mausb_ring_buffer.h | 53 + .../mausb_host/include/utils/mausb_version.h | 17 + drivers/staging/mausb_host/src/hcd/hub.c | 1801 +++++++++++++++++ .../staging/mausb_host/src/hcd/module_init.c | 239 +++ drivers/staging/mausb_host/src/hcd/vhcd.c | 216 ++ .../staging/mausb_host/src/hpal/data_common.c | 139 ++ drivers/staging/mausb_host/src/hpal/data_in.c | 82 + .../staging/mausb_host/src/hpal/data_out.c | 207 ++ drivers/staging/mausb_host/src/hpal/hpal.c | 1327 ++++++++++++ .../staging/mausb_host/src/hpal/isoch_in.c | 243 +++ .../staging/mausb_host/src/hpal/isoch_out.c | 362 ++++ .../mausb_host/src/hpal/mausb_events.c | 661 ++++++ .../mausb_host/src/hpal/network_callbacks.c | 162 ++ .../mausb_host/src/link/mausb_ip_link.c | 364 ++++ .../src/utils/mausb_data_iterator.c | 300 +++ .../staging/mausb_host/src/utils/mausb_mmap.c | 358 ++++ .../mausb_host/src/utils/mausb_ring_buffer.c | 140 ++ 39 files changed, 8700 insertions(+) create mode 100644 drivers/staging/mausb_host/Kconfig create mode 100644 drivers/staging/mausb_host/Makefile create mode 100644 drivers/staging/mausb_host/include/common/ma_usb.h create mode 100644 drivers/staging/mausb_host/include/common/mausb_address.h create mode 100644 drivers/staging/mausb_host/include/common/mausb_driver_status.h create mode 100644 drivers/staging/mausb_host/include/common/mausb_event.h create mode 100644 drivers/staging/mausb_host/include/hcd/hub.h create mode 100644 drivers/staging/mausb_host/include/hcd/vhcd.h create mode 100644 drivers/staging/mausb_host/include/hpal/data_common.h create mode 100644 drivers/staging/mausb_host/include/hpal/data_in.h create mode 100644 drivers/staging/mausb_host/include/hpal/data_out.h create mode 100644 drivers/staging/mausb_host/include/hpal/hpal.h create mode 100644 drivers/staging/mausb_host/include/hpal/isoch_in.h create mode 100644 drivers/staging/mausb_host/include/hpal/isoch_out.h create mode 100644 drivers/staging/mausb_host/include/hpal/mausb_events.h create mode 100644 drivers/staging/mausb_host/include/hpal/network_callbacks.h create mode 100644 drivers/staging/mausb_host/include/link/mausb_ip_link.h create mode 100644 drivers/staging/mausb_host/include/utils/mausb_data_iterator.h create mode 100644 drivers/staging/mausb_host/include/utils/mausb_logs.h create mode 100644 drivers/staging/mausb_host/include/utils/mausb_mmap.h create mode 100644 drivers/staging/mausb_host/include/utils/mausb_ring_buffer.h create mode 100644 drivers/staging/mausb_host/include/utils/mausb_version.h create mode 100644 drivers/staging/mausb_host/src/hcd/hub.c create mode 100644 drivers/staging/mausb_host/src/hcd/module_init.c create mode 100644 drivers/staging/mausb_host/src/hcd/vhcd.c create mode 100644 drivers/staging/mausb_host/src/hpal/data_common.c create mode 100644 drivers/staging/mausb_host/src/hpal/data_in.c create mode 100644 drivers/staging/mausb_host/src/hpal/data_out.c create mode 100644 drivers/staging/mausb_host/src/hpal/hpal.c create mode 100644 drivers/staging/mausb_host/src/hpal/isoch_in.c create mode 100644 drivers/staging/mausb_host/src/hpal/isoch_out.c create mode 100644 drivers/staging/mausb_host/src/hpal/mausb_events.c create mode 100644 drivers/staging/mausb_host/src/hpal/network_callbacks.c create mode 100644 drivers/staging/mausb_host/src/link/mausb_ip_link.c create mode 100644 drivers/staging/mausb_host/src/utils/mausb_data_iterator.c create mode 100644 drivers/staging/mausb_host/src/utils/mausb_mmap.c create mode 100644 drivers/staging/mausb_host/src/utils/mausb_ring_buffer.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index baccd7c883cc..cd9485ed98e7 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -113,6 +113,7 @@ source "drivers/staging/fieldbus/Kconfig" source "drivers/staging/kpc2000/Kconfig" source "drivers/staging/wusbcore/Kconfig" + source "drivers/staging/uwb/Kconfig" source "drivers/staging/exfat/Kconfig" @@ -123,4 +124,6 @@ source "drivers/staging/hp/Kconfig" source "drivers/staging/wfx/Kconfig" +source "drivers/staging/mausb_host/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index fdd03fd6e704..0c5c9b16c151 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -52,3 +52,4 @@ obj-$(CONFIG_STAGING_EXFAT_FS) += exfat/ obj-$(CONFIG_QLGE) += qlge/ obj-$(CONFIG_NET_VENDOR_HP) += hp/ obj-$(CONFIG_WFX) += wfx/ +obj-$(CONFIG_HOST_MAUSB) += mausb_host/ diff --git a/drivers/staging/mausb_host/Kconfig b/drivers/staging/mausb_host/Kconfig new file mode 100644 index 000000000000..957251591b53 --- /dev/null +++ b/drivers/staging/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 + bool "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/staging/mausb_host/Makefile b/drivers/staging/mausb_host/Makefile new file mode 100644 index 000000000000..8ef3b8c44bbd --- /dev/null +++ b/drivers/staging/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/staging/mausb_host/include/common/ma_usb.h b/drivers/staging/mausb_host/include/common/ma_usb.h new file mode 100644 index 000000000000..3d4b35dde458 --- /dev/null +++ b/drivers/staging/mausb_host/include/common/ma_usb.h @@ -0,0 +1,851 @@ +/* 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 <linux/types.h> +#else +#include <types.h> +#endif /* __KERNEL__ */ + +#define _MA_USB_SET_FIELD(_m, _v) (((~((_m) - 1) & (_m)) * (_v)) & (_m)) +#define _MA_USB_GET_FIELD(_m, _v) (((_v) & (_m)) / (~((_m) - 1) & (_m))) +#define MA_USB_SET_FIELD_(_m_, _v) _MA_USB_SET_FIELD(MA_USB_##_m_##_MASK, _v) +#define MA_USB_GET_FIELD_(_m_, _v) _MA_USB_GET_FIELD(MA_USB_##_m_##_MASK, _v) +#define MA_USB_SET_FIELD(_m_, _v) _MA_USB_SET_FIELD(MA_USB_##_m_##_MASK, \ + MA_USB_##_v) +#define MA_USB_GET_FIELD(_m_, _v) _MA_USB_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_IS_MANAGEMENT(_v) ( \ + MA_USB_GET_FIELD_(HDR_TYPE_TYPE, _v) \ + == MA_USB_HDR_TYPE_TYPE_MANAGEMENT) +#define MA_USB_HDR_TYPE_IS_MANAGEMENT_RESP(_v) ( \ + MA_USB_HDR_TYPE_IS_MANAGEMENT(_v) && \ + (MA_USB_GET_FIELD_(HDR_TYPE_SUBTYPE, _v) & 1) != 0) +#define MA_USB_HDR_TYPE_MANAGEMENT_GET_SUBTYPE(_v) ( \ + MA_USB_HDR_TYPE_IS_MANAGEMENT(_v) ? ((_v) >> 1) : -1) + +#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) ( \ + 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) ( \ + 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_IS_DATA(_v) ( \ + MA_USB_GET_FIELD_(HDR_TYPE_TYPE, _v) \ + == MA_USB_HDR_TYPE_TYPE_DATA) + +#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 (sizeof(struct ma_usb_hdr_common) +\ + sizeof(struct ma_usb_hdr_transfer)) + +#define MAUSB_ISOCH_TRANSFER_HDR_SIZE (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) ((uint8_t *)(ptr) + (offset)) + +/* USB descriptor */ +struct ma_usb_desc { + uint8_t length; + uint8_t type; + uint8_t value[0]; +} __packed; + +struct ma_usb_ep_handle { + uint16_t d :1, + ep_n :4, + addr :7, + bus_n :4; +}; + +struct ma_usb_hdr_mgmt { + uint32_t status :8, + token :10, /* requestor originator allocated */ + reserved :14; +} __aligned(4); + +struct ma_usb_hdr_ctrl { /* used in all req/resp/conf operations */ + int8_t status; + uint8_t link_type; + union { + uint8_t tid; /* ieee 802.11 */ + } connection_id; +} __aligned(4); + +struct ma_usb_hdr_data { + int8_t status; + uint8_t eps :2, + t_flags :6; + union { + uint16_t stream_id; + struct { + uint16_t headers :12, + i_flags :4; + }; + }; +} __aligned(4); + +struct ma_usb_hdr_common { + uint8_t version :4, + flags :4; + uint8_t type; + uint16_t length; + union { + uint16_t dev; + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint8_t dev_addr; + uint8_t ssid; + union { + int8_t 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 { + uint32_t out_mgmt_reqs :12, + reserved :20; +} __aligned(4); + +struct ma_usb_capreq_desc_synchronization { + uint8_t media_time_available :1, + reserved :7; +} __packed; + +struct ma_usb_capreq_desc_link_sleep { + uint8_t link_sleep_capable :1, + reserved :7; +} __packed; + +/* capresp extra hdr */ + +struct ma_usb_hdr_capresp { + uint16_t endpoints; + uint8_t devices; + uint8_t streams :5, + dev_type :3; + uint32_t descs :8, + descs_length :24; + uint16_t out_transfer_reqs; + uint16_t out_mgmt_reqs :12, + reserved :4; +} __aligned(4); + +struct ma_usb_capresp_desc_speed { + uint8_t reserved1 :4, + speed :4; + uint8_t reserved2 :4, + lse :2, /* USB3.1 8.5.6.7, Table 8-22 */ + reserved3 :2; +} __packed; + +struct ma_usb_capresp_desc_p_managed_out { + uint8_t elastic_buffer :1, + drop_notification :1, + reserved :6; +} __packed; + +struct ma_usb_capresp_desc_isochronous { + uint8_t payload_dword_aligned :1, + reserved :7; +} __packed; + +struct ma_usb_capresp_desc_synchronization { + uint8_t media_time_available :1, + time_stamp_required :1,/* hubs need this set */ + reserved :6; +} __packed; + +struct ma_usb_capresp_desc_container_id { + uint8_t container_id[16]; /* UUID IETF RFC 4122 */ +} __packed; + +struct ma_usb_capresp_desc_link_sleep { + uint8_t link_sleep_capable :1, + reserved :7; +} __packed; + +struct ma_usb_capresp_desc_hub_latency { + uint16_t latency; /* USB3.1 */ +} __packed; + +/* usbdevhandlereq extra hdr */ +struct ma_usb_hdr_usbdevhandlereq { + uint32_t route_string :20, + speed :4, + reserved1 :8; + uint16_t hub_dev_handle; + uint16_t reserved2; + uint16_t parent_hs_hub_dev_handle; + uint16_t 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 { + uint16_t dev_handle; + uint16_t reserved; +} __aligned(4); + +/* ephandlereq extra hdr */ +struct ma_usb_hdr_ephandlereq { + uint32_t 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 { + uint8_t bLength; + /* USB2.0 9.4, Table 9-5 (5) usb/ch9.h: USB_DT_ENDPOINT */ + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t 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 { + uint8_t bLength; + /* USB3.1 9.4, Table 9-6 (48) usb/ch9.h: USB_DT_SS_ENDPOINT_COMP */ + uint8_t bDescriptorType; + uint8_t bMaxBurst; + uint8_t bmAttributes; + uint16_t 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 { + uint8_t bLength; + /* USB3.1 9.4, Table 9-6 (49) usb/ch9.h: not yet defined! */ + uint8_t bDescriptorType; + uint16_t wReserved; + uint32_t 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 { + uint32_t ep_descs :5, + reserved :27; +} __aligned(4); + +/* ephandleresp descriptor */ +struct ma_usb_ephandleresp_desc { + union { + struct ma_usb_ep_handle eph; + uint16_t epv; + } ep_handle; + uint16_t d :1, /* non-control or non-OUT */ + isoch :1, + l_managed :1, /* control or non-isoch OUT */ + invalid :1, + reserved1 :12; + uint16_t ccu; /* control or non-isoch OUT */ + uint16_t reserved2; + uint32_t buffer_size; /* control or OUT */ + uint16_t isoch_prog_delay; /* in us. */ + uint16_t isoch_resp_delay; /* in us. */ +} __aligned(4); + +/* epactivatereq extra hdr */ +struct ma_usb_hdr_epactivatereq { + uint32_t ep_handles :5, + reserved :27; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epactivateresp extra hdr */ +struct ma_usb_hdr_epactivateresp { + uint32_t ep_errors :5, + reserved :27; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epinactivatereq extra hdr */ +struct ma_usb_hdr_epinactivatereq { + uint32_t ep_handles :5, + suspend :1, + reserved :26; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epinactivateresp extra hdr */ +struct ma_usb_hdr_epinactivateresp { + uint32_t ep_errors :5, + reserved :27; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* epresetreq extra hdr */ +struct ma_usb_hdr_epresetreq { + uint32_t ep_reset_blocks :5, + reserved :27; +} __aligned(4); + +/* epresetreq reset block */ +struct ma_usb_epresetreq_block { + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint16_t tsp :1, + reserved :15; +} __aligned(4); + +/* epresetresp extra hdr */ +struct ma_usb_hdr_epresetresp { + uint32_t ep_errors :5, + reserved :27; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* cleartransfersreq extra hdr */ +struct ma_usb_hdr_cleartransfersreq { + uint32_t info_blocks :8, + reserved :24; +} __aligned(4); + +/* cleartransfersreq info block */ +struct ma_usb_cleartransfersreq_block { + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint16_t stream_id; /* ss stream eps only */ + uint32_t start_req_id :8, + reserved :24; +} __aligned(4); + +/* cleartransfersresp extra hdr */ +struct ma_usb_hdr_cleartransfersresp { + uint32_t status_blocks :8, + reserved :24; +} __aligned(4); + +/* cleartransfersresp status block */ +struct ma_usb_cleartransfersresp_block { + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint16_t stream_id; /* ss stream eps only */ + uint32_t cancel_success :1, + partial_delivery :1, + reserved :30; + uint32_t last_req_id :8, + delivered_seq_n :24; /* OUT w/partial_delivery only */ + uint32_t delivered_byte_offset; /* OUT w/partial_delivery only */ +} __aligned(4); + +/* ephandledeletereq extra hdr */ +struct ma_usb_hdr_ephandledeletereq { + uint32_t ep_handles :5, + reserved :27; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* ephandledeleteresp extra hdr */ +struct ma_usb_hdr_ephandledeleteresp { + uint32_t ep_errors :5, + reserved :27; + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle[0]; +} __aligned(4); + +/* modifyep0req extra hdr */ +struct ma_usb_hdr_modifyep0req { + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint16_t 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 { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + + uint16_t reserved; +} __aligned(4); + +/* setusbdevaddrreq extra hdr */ +struct ma_usb_hdr_setusbdevaddrreq { + uint16_t response_timeout; /* in ms. */ + uint16_t reserved; +} __aligned(4); + +/* setusbdevaddrresp extra hdr */ +struct ma_usb_hdr_setusbdevaddrresp { + uint32_t addr :7, + reserved :25; +} __aligned(4); + +/* updatedevreq extra hdr */ +struct ma_usb_hdr_updatedevreq { + uint16_t max_exit_latency; /* hubs only */ + uint8_t hub :1, + ports :4, + mtt :1, + ttt :2; + uint8_t 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 { + uint8_t bLength; + uint8_t bDescriptorType;/* USB2.0 9.4, Table 9-5 (1) + * usb/ch9.h: USB_DT_DEVICE + */ + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} __packed; + +struct ma_usb_updatedevreq_desc { + struct usb_dev_desc usb20; +} __aligned(4); + +/* remotewakereq extra hdr */ +struct ma_usb_hdr_remotewakereq { + uint32_t resumed :1, + reserved :31; +} __aligned(4); + +/* synchreq/resp extra hdr */ +struct ma_usb_hdr_synch { + uint32_t mtd_valid :1, /* MA-USB1.0b 6.5.1.8, Table 66 */ + resp_required :1, + reserved :30; + union { + uint32_t timestamp; /* MA-USB1.0b 6.5.1.11 */ + struct { + uint32_t delta :13, + bus_interval :19; + }; /* MA-USB1.0b 6.6.1, Table 69 */ + }; + uint32_t mtd; /* MA-USB1.0b 6.5.1.12 */ +} __aligned(4); + +/* canceltransferreq extra hdr */ +struct ma_usb_hdr_canceltransferreq { + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint16_t stream_id; + uint32_t req_id :8, + reserved :24; +} __aligned(4); + +/* canceltransferresp extra hdr */ +struct ma_usb_hdr_canceltransferresp { + union { + uint16_t epv; + struct ma_usb_ep_handle eph; + } handle; + uint16_t stream_id; + uint32_t req_id :8, + cancel_status :3, + reserved1 :21; + uint32_t delivered_seq_n :24, + reserved2 :8; + uint32_t delivered_byte_offset; +} __aligned(4); + +/* transferreq/resp/ack extra hdr */ +struct ma_usb_hdr_transfer { + uint32_t seq_n :24, + req_id :8; + union { + uint32_t rem_size_credit; + /* ISOCH aliased fields added for convenience. */ + struct { + uint32_t presentation_time :20, + segments :12; + }; + }; +} __aligned(4); + +/* isochtransferreq/resp extra hdr */ +struct ma_usb_hdr_isochtransfer { + uint32_t seq_n :24, + req_id :8; + uint32_t presentation_time :20, + segments :12; +} __aligned(4); + +/* isochtransferreq/resp optional hdr */ +struct ma_usb_hdr_isochtransfer_optional { + union { + uint32_t timestamp; /* MA-USB1.0b 6.5.1.11 */ + struct { + uint32_t delta :13, + bus_interval :19; + }; /* MA-USB1.0b 6.6.1, Table 69 */ + }; + uint32_t mtd; /* MA-USB1.0b 6.5.1.12 */ +} __aligned(4); + +/* isochdatablock hdrs */ + +struct ma_usb_hdr_isochdatablock_short { + uint16_t block_length; + uint16_t segment_number :12, + s_flags :4; +} __aligned(4); + +struct ma_usb_hdr_isochdatablock_std { + uint16_t block_length; + uint16_t segment_number :12, + s_flags :4; + uint16_t segment_length; + uint16_t fragment_offset; +} __aligned(4); + +struct ma_usb_hdr_isochdatablock_long { + uint16_t block_length; + uint16_t segment_number :12, + s_flags :4; + uint32_t segment_length; + uint32_t fragment_offset; +} __aligned(4); + +/* isochreadsizeblock hdrs */ + +struct ma_usb_hdr_isochreadsizeblock_std { + uint32_t service_intervals :12, + max_segment_length :20; +} __aligned(4); + +struct ma_usb_hdr_isochreadsizeblock_long { + uint32_t service_intervals :12, + reserved :20; + uint32_t max_segment_length; +} __aligned(4); + +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/staging/mausb_host/include/common/mausb_address.h b/drivers/staging/mausb_host/include/common/mausb_address.h new file mode 100644 index 000000000000..4d785fd1b080 --- /dev/null +++ b/drivers/staging/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 <linux/types.h> +#else +#include <types.h> +#endif /* __KERNEL__ */ + +#define ADDR_LEN 16 + +struct mausb_device_address { + uint8_t linkType; + struct { + union { + char ip4[ADDR_LEN]; + uint8_t ip6[ADDR_LEN]; + } Address; + uint8_t numberOfPorts; + struct { + uint16_t management; + uint16_t control; + uint16_t bulk; + uint16_t interrupt; + uint16_t isochronous; + } Port; + } Ip; +}; + +#endif /* __MAUSB_COMMON_MAUSB_ADDRESS_H__ */ diff --git a/drivers/staging/mausb_host/include/common/mausb_driver_status.h b/drivers/staging/mausb_host/include/common/mausb_driver_status.h new file mode 100644 index 000000000000..1823a079908f --- /dev/null +++ b/drivers/staging/mausb_host/include/common/mausb_driver_status.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_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 + +#endif diff --git a/drivers/staging/mausb_host/include/common/mausb_event.h b/drivers/staging/mausb_host/include/common/mausb_event.h new file mode 100644 index 000000000000..c8aa2b41605b --- /dev/null +++ b/drivers/staging/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 <common/ma_usb.h> + +#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 { + uint64_t event_id; + uint32_t route_string; + uint16_t hub_dev_handle; + uint16_t parent_hs_hub_dev_handle; + uint16_t parent_hs_hub_port; + uint16_t mtt; + /* dev_handle assigned in user */ + uint16_t dev_handle; + uint8_t device_speed; + uint8_t lse; +}; + +struct mausb_ephandle { + uint64_t event_id; + uint16_t device_handle; + uint16_t descriptor_size; + /* ep_handle assigned in user */ + uint16_t ep_handle; + char descriptor[sizeof(struct ma_usb_ephandlereq_desc_ss)]; +}; + +struct mausb_epactivate { + uint64_t event_id; + uint16_t device_handle; + uint16_t ep_handle; +}; + +struct mausb_epinactivate { + uint64_t event_id; + uint16_t device_handle; + uint16_t ep_handle; +}; + +struct mausb_epreset { + uint64_t event_id; + uint16_t device_handle; + uint16_t ep_handle; + uint8_t tsp; +}; + +struct mausb_epdelete { + uint64_t event_id; + uint16_t device_handle; + uint16_t ep_handle; +}; + +struct mausb_updatedev { + uint64_t event_id; + uint16_t device_handle; + uint16_t max_exit_latency; + struct ma_usb_updatedevreq_desc update_descriptor; + uint8_t hub; + uint8_t number_of_ports; + uint8_t mtt; + uint8_t ttt; + uint8_t integrated_hub_latency; +}; + +struct mausb_usbdevreset { + uint64_t event_id; + uint16_t device_handle; +}; + +struct mausb_modifyep0 { + uint64_t event_id; + uint16_t device_handle; + uint16_t ep_handle; + uint16_t max_packet_size; +}; + +struct mausb_setusbdevaddress { + uint64_t event_id; + uint16_t device_handle; + uint16_t response_timeout; +}; + +struct mausb_usbdevdisconnect { + uint16_t device_handle; +}; + +struct mausb_canceltransfer { + uint64_t urb; + uint16_t device_handle; + uint16_t ep_handle; +}; + +struct mausb_mgmt_hdr { + __aligned(4) char hdr[MAUSB_MAX_MGMT_SIZE]; +}; + +struct mausb_mgmt_req_timedout { + uint64_t event_id; +}; + +struct mausb_delete_ma_dev { + uint64_t event_id; + uint16_t 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 { + uint64_t urb; + uint64_t recv_buf; + uint32_t iterator_seek_delta; + uint32_t transfer_size; + uint32_t rem_transfer_size; + uint32_t transfer_flags; + uint32_t isoch_seg_num; + uint32_t req_id; + uint32_t payload_size; + int32_t status; + + __aligned(4) char hdr[MAUSB_TRANSFER_HDR_SIZE]; + __aligned(4) char hdr_ack[MAUSB_TRANSFER_HDR_SIZE]; + + uint16_t device_id; + uint16_t ep_handle; + uint16_t packet_size; + uint8_t setup_packet; + uint8_t direction; + uint8_t transfer_type; + uint8_t first_control_packet; + uint8_t transfer_eot; + uint8_t mausb_address; + uint8_t mausb_ssid; +}; + +struct mausb_port_changed_event { + uint8_t port; + uint8_t dev_type; + uint8_t dev_speed; + uint8_t lse; +}; + +struct mausb_event { + union { + struct mausb_mgmt_event mgmt; + struct mausb_data_event data; + struct mausb_port_changed_event port_changed; + }; + int32_t status; + uint8_t type; + uint8_t madev_addr; +}; + +struct mausb_events_notification { + uint16_t num_of_events; + uint16_t num_of_completed_events; + uint8_t madev_addr; +}; + +#endif /* __MAUSB_COMMON_MAUSB_EVENT_H__ */ diff --git a/drivers/staging/mausb_host/include/hcd/hub.h b/drivers/staging/mausb_host/include/hcd/hub.h new file mode 100644 index 000000000000..9b7390485705 --- /dev/null +++ b/drivers/staging/mausb_host/include/hcd/hub.h @@ -0,0 +1,120 @@ +/* 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 <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "utils/mausb_data_iterator.h" + +static const char driver_name[] = "MA-USB host controller"; + +#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 { + int32_t dev_handle; + bool addressed; + void *dev_addr; + struct rb_node rb_node; +}; + +struct mausb_endpoint_ctx { + uint16_t ep_handle; + uint16_t 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, uint32_t 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(uint16_t port_number); + + +#endif /* __MAUSB_HCD_HUB_H__ */ + diff --git a/drivers/staging/mausb_host/include/hcd/vhcd.h b/drivers/staging/mausb_host/include/hcd/vhcd.h new file mode 100644 index 000000000000..4e74a749e89d --- /dev/null +++ b/drivers/staging/mausb_host/include/hcd/vhcd.h @@ -0,0 +1,81 @@ +/* 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 <linux/device.h> +#include <linux/fs.h> +#include <linux/major.h> +#include <linux/proc_fs.h> +#include <linux/rbtree.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#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 + +struct mausb_hcd { + spinlock_t lock; + struct device *pdev; + uint16_t 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 { + uint32_t port_status; + struct rb_root usb_devices; + uint8_t dev_speed; + void *ma_dev; +}; + +struct hub_ctx { + struct mausb_hcd *mhcd; + struct usb_hcd *hcd; + struct mausb_dev ma_devs[NUMBER_OF_PORTS]; +}; + +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 +}; + +extern struct mausb_hcd *mhcd; + +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 uint16_t 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/staging/mausb_host/include/hpal/data_common.h b/drivers/staging/mausb_host/include/hpal/data_common.h new file mode 100644 index 000000000000..7b7bb4848c9f --- /dev/null +++ b/drivers/staging/mausb_host/include/hpal/data_common.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_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 uint8_t mausb_transfer_type_from_usb( + struct usb_endpoint_descriptor *epd) +{ + return (uint8_t)usb_endpoint_type(epd) << 3; +} + +static inline uint8_t 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( + uint8_t transfer_type) +{ + return transfer_type >> 3; +} + +#endif /* __MAUSB_HPAL_DATA_COMMON_H__ */ diff --git a/drivers/staging/mausb_host/include/hpal/data_in.h b/drivers/staging/mausb_host/include/hpal/data_in.h new file mode 100644 index 000000000000..e1e068313f66 --- /dev/null +++ b/drivers/staging/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); +int mausb_receive_in_data(struct mausb_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); + +#endif /* __MAUSB_HPAL_DATA_IN_H__ */ diff --git a/drivers/staging/mausb_host/include/hpal/data_out.h b/drivers/staging/mausb_host/include/hpal/data_out.h new file mode 100644 index 000000000000..809e972a9728 --- /dev/null +++ b/drivers/staging/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 <linux/types.h> + +#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); +int mausb_receive_out_data(struct mausb_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); + +#endif /* __MAUSB_HPAL_DATA_OUT_H__ */ diff --git a/drivers/staging/mausb_host/include/hpal/hpal.h b/drivers/staging/mausb_host/include/hpal/hpal.h new file mode 100644 index 000000000000..29c635da6a0a --- /dev/null +++ b/drivers/staging/mausb_host/include/hpal/hpal.h @@ -0,0 +1,174 @@ +/* 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 <linux/kref.h> +#include <linux/suspend.h> +#include <linux/usb.h> + +#include "common/mausb_address.h" +#include "common/mausb_event.h" +#include "link/mausb_ip_link.h" + +#define MAUSB_CONTROL_SETUP_SIZE 8 +#define MAUSB_BUSY_RETRIES_COUNT 3 +#define MAUSB_BUSY_TIMEOUT_MIN 10000 +#define MAUSB_BUSY_TIMEOUT_MAX 10001 +#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 + +enum mausb_isoch_header_format_size { + ISOCH_SHORT_FORMAT_SIZE = 4, + ISOCH_STANDARD_FORMAT_SIZE = 8, + ISOCH_LONG_FORMAT_SIZE = 12 +}; + +struct mausb_completion { + struct list_head list_entry; + struct completion *completion_event; + struct mausb_event *mausb_event; + long 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; + uint64_t ring_buffer_id; + + struct completion empty; + struct completion client_stopped; + bool client_connected; + struct timer_list heartbeat_timer; + uint8_t 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 */ + uint8_t dev_type; + uint8_t dev_speed; + uint8_t lse; + uint8_t madev_addr; + uint8_t dev_connected; + + uint16_t id; + uint16_t port_number; + + struct list_head completion_events; + atomic_long_t event_id; + spinlock_t completion_events_lock; + + struct completion user_finished_event; + uint32_t num_of_user_events; + spinlock_t num_of_user_events_lock; + + struct timer_list connection_timer; + uint8_t receive_failures_num; + spinlock_t connection_timer_lock; + + 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(uint8_t madev_addr); + +static inline long mausb_event_id(struct mausb_device *dev) +{ + long val = atomic_long_inc_return(&dev->event_id); + return val; +} + +int mausb_initiate_dev_connection(struct mausb_device_address device_address, + uint8_t madev_address); +int mausb_enqueue_event_from_user(uint8_t madev_addr, uint32_t num_of_events); +int mausb_enqueue_event_to_user(struct mausb_device *dev, + struct mausb_event *event); +int mausb_data_req_enqueue_event(struct mausb_device *dev, uint16_t ep_handle, + struct urb *request); +int mausb_signal_event(struct mausb_device *dev, struct mausb_event *event, + long 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); +} + +/* After this function call only valid thing to do with urb is to give it back*/ +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, uint32_t 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/staging/mausb_host/include/hpal/isoch_in.h b/drivers/staging/mausb_host/include/hpal/isoch_in.h new file mode 100644 index 000000000000..c09659342578 --- /dev/null +++ b/drivers/staging/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/staging/mausb_host/include/hpal/isoch_out.h b/drivers/staging/mausb_host/include/hpal/isoch_out.h new file mode 100644 index 000000000000..3a0741cee75f --- /dev/null +++ b/drivers/staging/mausb_host/include/hpal/isoch_out.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_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_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx); + +#endif /* __MAUSB_HPAL_ISOCH_OUT_H__ */ diff --git a/drivers/staging/mausb_host/include/hpal/mausb_events.h b/drivers/staging/mausb_host/include/hpal/mausb_events.h new file mode 100644 index 000000000000..543118ea1955 --- /dev/null +++ b/drivers/staging/mausb_host/include/hpal/mausb_events.h @@ -0,0 +1,97 @@ +/* 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, + uint8_t device_speed, + uint32_t route_string, + uint16_t hub_dev_handle, + uint16_t parent_hs_hub_dev_handle, + uint16_t parent_hs_hub_port, uint16_t mtt, + uint8_t lse, int32_t *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, + uint16_t device_handle, + uint16_t descriptor_size, void *descriptor, + uint16_t *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, + uint16_t device_handle, uint16_t 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, + uint16_t device_handle, + uint16_t 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, + uint16_t device_handle, uint16_t ep_handle, + uint8_t 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, + uint16_t device_handle, uint16_t 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, + uint16_t device_handle, uint16_t *ep_handle, + uint16_t 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, + uint16_t device_handle, + uint16_t 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, + uint16_t device_handle, + uint16_t max_exit_latency, uint8_t hub, + uint8_t number_of_ports, uint8_t mtt, + uint8_t ttt, uint8_t 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, + uint16_t dev_handle); +int mausb_ping_event_to_user(struct mausb_device *dev); +int mausb_usbdevreset_event_to_user(struct mausb_device *dev, + uint16_t 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, + uint16_t device_handle, + uint16_t ep_handle, uint64_t 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/staging/mausb_host/include/hpal/network_callbacks.h b/drivers/staging/mausb_host/include/hpal/network_callbacks.h new file mode 100644 index 000000000000..a10b590d53aa --- /dev/null +++ b/drivers/staging/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 <linux/workqueue.h> + +#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/staging/mausb_host/include/link/mausb_ip_link.h b/drivers/staging/mausb_host/include/link/mausb_ip_link.h new file mode 100644 index 000000000000..386358b59afb --- /dev/null +++ b/drivers/staging/mausb_host/include/link/mausb_ip_link.h @@ -0,0 +1,89 @@ +/* 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 <linux/inet.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <net/net_namespace.h> + +#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; + uint32_t kvec_num; + uint32_t length; +}; + +struct mausb_ip_recv_ctx { + uint16_t left; + uint16_t 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]; + uint16_t port; + bool udp; + /* IPV6 support */ + + /* 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; + /* recv buffer */ + struct mausb_ip_recv_ctx recv_ctx; + + 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], + uint16_t 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/staging/mausb_host/include/utils/mausb_data_iterator.h b/drivers/staging/mausb_host/include/utils/mausb_data_iterator.h new file mode 100644 index 000000000000..ae679272c08c --- /dev/null +++ b/drivers/staging/mausb_host/include/utils/mausb_data_iterator.h @@ -0,0 +1,55 @@ +/* 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 <linux/list.h> +#include <linux/scatterlist.h> +#include <linux/uio.h> + +struct mausb_data_iter { + + uint32_t length; + + void *buffer; + uint32_t buffer_len; + uint32_t offset; + + struct scatterlist *sg; + struct sg_mapping_iter sg_iter; + bool sg_started; + uint32_t 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, + uint32_t byte_num, + struct list_head *data_chunks_list, + uint32_t *data_chunks_num); + +uint32_t mausb_data_iterator_length(struct mausb_data_iter *iterator); +uint32_t mausb_data_iterator_write(struct mausb_data_iter *iterator, + void *buffer, uint32_t length); + +void mausb_init_data_iterator(struct mausb_data_iter *iterator, + void *buffer, uint32_t buffer_len, + struct scatterlist *sg, uint32_t 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, + uint32_t seek_delta); + + +#endif /* __MAUSB_UTILS_MAUSB_DATA_ITERATOR_H__ */ diff --git a/drivers/staging/mausb_host/include/utils/mausb_logs.h b/drivers/staging/mausb_host/include/utils/mausb_logs.h new file mode 100644 index 000000000000..de617528e028 --- /dev/null +++ b/drivers/staging/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 <linux/sched.h> +#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/staging/mausb_host/include/utils/mausb_mmap.h b/drivers/staging/mausb_host/include/utils/mausb_mmap.h new file mode 100644 index 000000000000..45b81f6f4c70 --- /dev/null +++ b/drivers/staging/mausb_host/include/utils/mausb_mmap.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_UTILS_MAUSB_MMAP_H__ +#define __MAUSB_UTILS_MAUSB_MMAP_H__ + +extern struct mss mss; +extern struct mausb_ring_buffer mausb_ring; + +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/staging/mausb_host/include/utils/mausb_ring_buffer.h b/drivers/staging/mausb_host/include/utils/mausb_ring_buffer.h new file mode 100644 index 000000000000..c92334b55072 --- /dev/null +++ b/drivers/staging/mausb_host/include/utils/mausb_ring_buffer.h @@ -0,0 +1,53 @@ +/* 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 <linux/spinlock.h> + +#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; + uint64_t id; + + struct mausb_event *from_user_buffer; + int current_from_user; + + struct list_head list_entry; +}; + +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, uint32_t count); + +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); +} + +void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring); +void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring); + +#endif /* ___MAUSB_UTILS_MAUSB_RING_BUFFER_H__ */ diff --git a/drivers/staging/mausb_host/include/utils/mausb_version.h b/drivers/staging/mausb_host/include/utils/mausb_version.h new file mode 100644 index 000000000000..6adf7febe916 --- /dev/null +++ b/drivers/staging/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.3857d02a-vladast-DEVEL) + +#endif /* __MAUSB_UTILS_MAUSB_VERSION_H__ */ diff --git a/drivers/staging/mausb_host/src/hcd/hub.c b/drivers/staging/mausb_host/src/hcd/hub.c new file mode 100644 index 000000000000..018d20d337c1 --- /dev/null +++ b/drivers/staging/mausb_host/src/hcd/hub.c @@ -0,0 +1,1801 @@ +// 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 <linux/version.h> + +#include "hcd/vhcd.h" +#include "hpal/hpal.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_logs.h" + +static void mausb_get_hub_descriptor(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, + char *buff, uint16_t wLength); +static void mausb_set_port_feature(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength); +static void mausb_get_port_status(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength); +static void mausb_clear_port_feature(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, + char *buff, uint16_t wLength); +static void mausb_get_hub_status(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength); +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, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength); +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=%d, 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 != 0, request->ep != 0, request->sg != 0, + request->num_sgs, request->num_mapped_sgs, request->status, + request->transfer_buffer != 0, request->transfer_buffer_length, + request->transfer_dma != 0, request->transfer_flags, + request->ep ? request->ep->hcpriv != 0 : 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 int8_t get_root_hub_port_number(struct usb_device *dev) +{ + struct usb_device *first_hub_device = dev; + + if (!dev->parent) { + mausb_pr_info("Trying to get roothub port number"); + return -1; + } + + while (first_hub_device->parent->parent) + first_hub_device = first_hub_device->parent; + + return first_hub_device->portnum - 1; +} + +static int usb_to_mausb_device_speed(uint8_t 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), + *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; +} + +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; + uint16_t 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 uint16_t port_number, + const enum mausb_device_type device_type, + const enum mausb_device_speed device_speed) +{ + struct usb_hcd *hcd; + unsigned long flags; + + if (port_number < 0 || 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); +} + +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 = ~0; + + return 0; +} + +static int mausb_hcd_start(struct usb_hcd *hcd) +{ + mausb_pr_info(""); + + 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; + + hub = (struct hub_ctx *)hcd->hcd_priv; + + retval = DIV_ROUND_UP(NUMBER_OF_PORTS + 1, 8); + changed = 0; + + memset(buff, 0, 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; + + mausb_pr_info(""); + + 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; + + mausb_pr_info(""); + + 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, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength) +{ + int retval; + struct mausb_hcd *mhcd; + struct hub_ctx *hub; + unsigned long flags; + bool invalid_rhport = false; + + hub = (struct hub_ctx *)hcd->hcd_priv; + mhcd = hub->mhcd; + + wIndex = ((__u8) (wIndex & 0x00ff)); + retval = 0; + + if (wIndex < 1 || wIndex > NUMBER_OF_PORTS) + invalid_rhport = true; + + mausb_pr_info("TypeReq=%d", typeReq); + + 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 -ETIMEDOUT; + } + + switch (typeReq) { + case ClearHubFeature: + break; + case ClearPortFeature: + if (invalid_rhport) + goto invalid_port; + + mausb_clear_port_feature(hcd, typeReq, wValue, wIndex, buff, + wLength); + 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, typeReq, wValue, wIndex, buff, + wLength); + break; + case GetHubStatus: + mausb_get_hub_status(hcd, typeReq, wValue, wIndex, buff, + wLength); + break; + case GetPortStatus: + if (invalid_rhport) + goto invalid_port; + + mausb_get_port_status(hcd, typeReq, wValue, wIndex, buff, + wLength); + break; + case SetHubFeature: + retval = -EPIPE; + break; + case SetPortFeature: + if (invalid_rhport) + goto invalid_port; + + mausb_set_port_feature(hcd, typeReq, wValue, wIndex, buff, + wLength); + break; + default: + retval = -EPIPE; + } + +invalid_port: + spin_unlock_irqrestore(&mhcd->lock, flags); + return retval; +} + +static int mausb_validate_urb(struct urb *urb) +{ + 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, uint32_t actual_length, int status) +{ + struct mausb_endpoint_ctx *endpoint_ctx; + 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); + + endpoint_ctx = urb->ep->hcpriv; + urb->status = status; + urb->actual_length = actual_length; + + if (endpoint_ctx) + mausb_pr_debug("ep_handle=%#x; urb->status=%d; urb->act_len=%d, dev_handle=%#x", + endpoint_ctx->ep_handle, urb->status, + urb->actual_length, + endpoint_ctx->dev_handle); + + 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, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, + char *buff, uint16_t wLength) +{ + int 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 = 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 = 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, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength) +{ + struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv; + + wIndex = ((__u8) (wIndex & 0x00ff)); + + switch (wValue) { + case USB_PORT_FEAT_LINK_STATE: + mausb_pr_info("USB_PORT_FEAT_LINK_STATE"); + if (hcd->speed == HCD_USB3) { + if ((hub->ma_devs[wIndex - 1].port_status & + USB_SS_PORT_STAT_POWER) != 0) { + hub->ma_devs[wIndex - 1].port_status |= + (1 << wValue); + } + } else { + if ((hub->ma_devs[wIndex - 1].port_status & + USB_PORT_STAT_POWER) != 0) { + hub->ma_devs[wIndex - 1].port_status |= + (1 << wValue); + } + } + 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[wIndex - 1].port_status |= + USB_SS_PORT_STAT_POWER; + } else { + hub->ma_devs[wIndex - 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[wIndex - 1].port_status = 0; + hub->ma_devs[wIndex - 1].port_status = + (USB_SS_PORT_STAT_POWER | + USB_PORT_STAT_CONNECTION | USB_PORT_STAT_RESET); + } else if (hub->ma_devs[wIndex - + 1].port_status & USB_PORT_STAT_ENABLE) { + hub->ma_devs[wIndex - 1].port_status &= + ~(USB_PORT_STAT_ENABLE | + USB_PORT_STAT_LOW_SPEED | + USB_PORT_STAT_HIGH_SPEED); + } + /* fall through */ + default: + mausb_pr_info("Default wValue=%d", wValue); + + if (hcd->speed == HCD_USB3) { + if ((hub->ma_devs[wIndex - 1].port_status & + USB_SS_PORT_STAT_POWER) != 0) { + hub->ma_devs[wIndex - 1].port_status |= + (1 << wValue); + } + } else { + if ((hub->ma_devs[wIndex - 1].port_status & + USB_PORT_STAT_POWER) != 0) { + hub->ma_devs[wIndex - 1].port_status |= + (1 << wValue); + } + } + } +} + +static void mausb_get_port_status(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength) +{ + uint8_t dev_speed = 0; + struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv; + + wIndex = ((__u8) (wIndex & 0x00ff)); + + if ((hub->ma_devs[wIndex - 1].port_status & + (1 << USB_PORT_FEAT_RESET)) != 0) { + mausb_pr_info("Finished reset hcd->speed=%d", hcd->speed); + + dev_speed = hub->ma_devs[wIndex - 1].dev_speed; + switch (dev_speed) { + case LOW_SPEED: + hub->ma_devs[wIndex - 1].port_status |= + USB_PORT_STAT_LOW_SPEED; + break; + case HIGH_SPEED: + hub->ma_devs[wIndex - 1].port_status |= + USB_PORT_STAT_HIGH_SPEED; + break; + } + + hub->ma_devs[wIndex - 1].port_status |= + (1 << USB_PORT_FEAT_C_RESET) | USB_PORT_STAT_ENABLE; + hub->ma_devs[wIndex - 1].port_status &= + ~(1 << USB_PORT_FEAT_RESET); + + } + + ((__le16 *) buff)[0] = + cpu_to_le16(hub->ma_devs[wIndex - 1].port_status); + ((__le16 *) buff)[1] = + cpu_to_le16(hub->ma_devs[wIndex - 1].port_status >> 16); + + mausb_pr_info("port_status=%d", hub->ma_devs[wIndex - 1].port_status); +} + +static void mausb_clear_port_feature(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, + char *buff, uint16_t wLength) +{ + struct hub_ctx *hub = (struct hub_ctx *)hcd->hcd_priv; + + wIndex = ((__u8) (wIndex & 0x00ff)); + + switch (wValue) { + 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[wIndex - 1].port_status &= + ~USB_SS_PORT_STAT_POWER; + } else { + hub->ma_devs[wIndex - 1].port_status &= + ~USB_PORT_STAT_POWER; + } + break; + case USB_PORT_FEAT_RESET: + + case USB_PORT_FEAT_C_RESET: + + default: + mausb_pr_info("Default wValue: %d", wValue); + + hub->ma_devs[wIndex - 1].port_status &= ~(1 << wValue); + } +} + +static void mausb_get_hub_status(struct usb_hcd *hcd, uint16_t typeReq, + uint16_t wValue, uint16_t wIndex, char *buff, + uint16_t wLength) +{ + mausb_pr_info(""); + *(uint32_t *) 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) +{ + int8_t port_number = get_root_hub_port_number(dev); + int32_t dev_handle; + int status = 0; + 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 *endpoint_ctx = dev->ep0.hcpriv; + + if (port_number < 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(endpoint_ctx); + goto l_free_dev; + } + + if (endpoint_ctx) { + status = mausb_epinactivate_event_to_user(ma_dev, dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("epinactivate request ep_handle=%#x, dev_handle=%#x, status=%d", + endpoint_ctx->ep_handle, dev_handle, status); + + status = mausb_epdelete_event_to_user(ma_dev, dev_handle, + endpoint_ctx->ep_handle); + + if (status < 0) + mausb_pr_warn("ep_handle_del request failed for ep0: ep_handle=%#x, dev_handle=%#x", + endpoint_ctx->ep_handle, dev_handle); + dev->ep0.hcpriv = NULL; + kfree(endpoint_ctx); + + } else { + mausb_pr_warn("endpoint_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); + } + +l_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_device_ctx) +{ + int status = 0; + + status = mausb_setusbdevaddress_event_to_user(dev, + usb_device_ctx->dev_handle, RESPONSE_TIMEOUT); + + mausb_pr_info("dev_handle=%#x, status=%d", usb_device_ctx->dev_handle, + status); + usb_device_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, + uint16_t port_number, + int *status) +{ + struct mausb_usb_device_ctx *usb_device_ctx = NULL; + + mausb_pr_info(""); + + usb_device_ctx = kzalloc(sizeof(*usb_device_ctx), GFP_ATOMIC); + if (unlikely(!usb_device_ctx)) { + mausb_pr_err("Allocation failed"); + *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) +{ + int8_t port_number = 0; + int status = 0; + 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; + + port_number = get_root_hub_port_number(dev); + + if (port_number < 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 = 0; + int8_t port_number = 0; + 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_device_ctx; + struct mausb_endpoint_ctx *endpoint_ctx; + unsigned long flags; + + port_number = get_root_hub_port_number(dev); + + if (port_number < 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_warn("Device not found"); + return -ENODEV; + } + + endpoint_ctx = kzalloc(sizeof(*endpoint_ctx), GFP_ATOMIC); + if (unlikely(!endpoint_ctx)) { + mausb_pr_alert("Failed to alloc endpoint_ctx"); + return -ENOMEM; + } + + endpoint_ctx->dev_handle = usb_device_ctx->dev_handle; + endpoint_ctx->usb_device_ctx = usb_device_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_device_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_device_ctx->dev_handle, + sizeof(descriptor), + &descriptor, + &endpoint_ctx->ep_handle); + } + + if (status < 0) { + mausb_pr_err("ep_handle_request failed dev_handle=%#x", + usb_device_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) +{ + int8_t port_number = 0; + int status = 0, + retries = 0; + 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 = endpoint->hcpriv; + unsigned long flags; + + port_number = get_root_hub_port_number(dev); + + if (port_number < 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 (!endpoint_ctx) { + mausb_pr_err("Endpoint context doesn't exist"); + return 0; + } + if (!usb_device_ctx) { + mausb_pr_err("Usb device context doesn't exist"); + return 0; + } + + 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 status; + } + + status = mausb_epinactivate_event_to_user(ma_dev, + usb_device_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_device_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); + /* sleep for 10 ms to give device some time */ + if (status == -EBUSY) { + if (++retries < MAUSB_BUSY_RETRIES_COUNT) + usleep_range(MAUSB_BUSY_TIMEOUT_MIN, + MAUSB_BUSY_TIMEOUT_MAX); + else + return status; + } 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) +{ + int8_t port_number = get_root_hub_port_number(dev); + int status = 0; + int dev_speed = 0; + uint16_t hub_dev_handle = 0; + uint16_t parent_hs_hub_dev_handle = 0; + uint16_t 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; + + if (port_number < 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 (unlikely(dev_speed == -EINVAL)) { + mausb_pr_err("bad dev_speed"); + return -EINVAL; + } + + status = mausb_usbdevhandle_event_to_user(ma_dev, (uint8_t) 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 (unlikely(!endpoint_ctx)) { + mausb_pr_alert("Failed to allocate 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) +{ + int8_t 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; + int status = 0; + + port_number = get_root_hub_port_number(dev); + + if (port_number < 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) +{ + int8_t 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; + + if (mausb_is_hub_device(dev)) { + mausb_pr_warn("Device is hub"); + return 0; + } + + port_number = get_root_hub_port_number(dev); + + if (port_number < 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) +{ + int8_t port_number = 0; + int status = 0; + unsigned long flags; + uint16_t max_exit_latency = 0; + uint8_t number_of_ports = dev->maxchild; + uint8_t mtt = 0; + uint8_t ttt = 0; + uint8_t 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 = tt->think_time; + } + port_number = get_root_hub_port_number(dev); + + if (port_number < 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 = dev->u1_params.mel; + else if (dev->usb3_lpm_u2_enabled) + max_exit_latency = 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 = 0; + int is_control, + epnum, + is_out; + unsigned long flags; + uint16_t dev_handle; + uint8_t tsp; + int8_t 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 *endpoint_ctx; + struct ma_usb_ephandlereq_desc_ss descriptor_ss; + struct ma_usb_ephandlereq_desc_std descriptor; + + endpoint_ctx = endpoint->hcpriv; + if (!endpoint_ctx) { + mausb_pr_err("ep->hcpriv is NULL"); + return; + } + + usb_device_ctx = endpoint_ctx->usb_device_ctx; + dev_handle = usb_device_ctx->dev_handle; + dev = usb_device_ctx->dev_addr; + + port_number = get_root_hub_port_number(dev); + + if (port_number < 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 = is_out ? dev->toggle[1] : dev->toggle[0]; + + status = mausb_epreset_event_to_user(ma_dev, dev_handle, + endpoint_ctx->ep_handle, tsp); + + mausb_pr_info("ep_reset status=%d, ep_handle=%#x, dev_handle=%#x", + status, endpoint_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + + if (status != EUCLEAN) { + if (!tsp) { + usb_settoggle(dev, epnum, is_out, 0); + if (is_control) + usb_settoggle(dev, epnum, !is_out, 0); + } + + status = mausb_epactivate_event_to_user(ma_dev, dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_err("ep_activate failed, status=%d, ep_handle=%#x, dev_handle=%#x", + status, endpoint_ctx->ep_handle, dev_handle); + + return; + } + + if (tsp) + return; + + status = mausb_epinactivate_event_to_user(ma_dev, dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("ep_inactivate status=%d, ep_handle=%#x, dev_handle=%#x", + status, endpoint_ctx->ep_handle, dev_handle); + + if (status < 0) + return; + + status = mausb_epdelete_event_to_user(ma_dev, dev_handle, + endpoint_ctx->ep_handle); + + mausb_pr_info("ep_handle_delete status=%d, ep_handle=%#x, dev_handle=%#x", + status, endpoint_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, + &endpoint_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, + &endpoint_ctx->ep_handle); + } + + mausb_pr_info("ep_handle request status=%d, ep_handle=%#x, dev_handle=%#x", + status, endpoint_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) +{ + int8_t port_number = 0; + uint16_t dev_handle; + int status = 0; + 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; + port_number = get_root_hub_port_number(dev); + + if (port_number < 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(uint16_t 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/staging/mausb_host/src/hcd/module_init.c b/drivers/staging/mausb_host/src/hcd/module_init.c new file mode 100644 index 000000000000..7ac24c409156 --- /dev/null +++ b/drivers/staging/mausb_host/src/hcd/module_init.c @@ -0,0 +1,239 @@ +// 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 <linux/in.h> +#include <linux/inet.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/net.h> + +#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 uint64_t mausb_event_count_param; +static uint16_t madev_addr; +static uint8_t mausb_client_connect_param; +static uint8_t 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; + + 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; + 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) +{ + uint8_t device_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, &device_address); + if (status < 0) + return -EINVAL; + + spin_lock_irqsave(&mss.lock, flags); + + dev = mausb_get_dev_from_addr_unsafe(device_address); + if (dev) + queue_work(dev->workq, &dev->hcd_disconnect_work); + + spin_unlock_irqrestore(&mss.lock, flags); + + return 0; +} + +static int mausb_event_param(const char *value, + const struct kernel_param *kp) +{ + uint64_t mausb_addr_and_event_count = 0; + int status = kstrtou64(value, 0, &mausb_addr_and_event_count); + uint32_t mausb_address; + uint32_t mausb_event_count; + + if (status < 0) + return -EINVAL; + + mausb_address = (mausb_addr_and_event_count >> 8 * sizeof(uint32_t)); + mausb_event_count = (uint32_t) mausb_addr_and_event_count; + mausb_pr_debug("value=%s, mausb_event_count=%#x", + value, mausb_event_count); + + mausb_enqueue_event_from_user(mausb_address, mausb_event_count); + 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 +}; + +static const struct kernel_param_ops mausb_event_ops = { + .set = mausb_event_param +}; + +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); +module_param_cb(mausb_event, &mausb_event_ops, &mausb_event_count_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/staging/mausb_host/src/hcd/vhcd.c b/drivers/staging/mausb_host/src/hcd/vhcd.c new file mode 100644 index 000000000000..bbf07c095cbf --- /dev/null +++ b/drivers/staging/mausb_host/src/hcd/vhcd.c @@ -0,0 +1,216 @@ +// 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 <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "hcd/hub.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) +{ + mausb_pr_info(""); + return mausb_probe(dev); +} + +static int mausb_bus_remove(struct device *dev) +{ + mausb_pr_info(""); + return 0; +} + +static int mausb_bus_match(struct device *dev, struct device_driver *drv) +{ + mausb_pr_info(""); + + 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) +{ + mausb_pr_info(""); + return 0; +} + +static int mausb_release(struct inode *inode, struct file *file) +{ + mausb_pr_info(""); + return 0; +} + +static ssize_t mausb_read(struct file *file, char __user *buffer, size_t length, + loff_t *offset) +{ + mausb_pr_info(""); + return 0; +} + +static ssize_t mausb_write(struct file *file, const char __user *buffer, + size_t length, loff_t *offset) +{ + mausb_pr_info(""); + return 0; +} + +static long mausb_ioctl(struct file *file, unsigned int ioctl_func, + unsigned long ioctl_buffer) +{ + mausb_pr_info(""); + 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 = 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 (unlikely(!mhcd)) { + mausb_pr_alert("Mausb_hcd allocation failed"); + 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); + kfree(mhcd); + } + + mausb_pr_info("Finish"); +} diff --git a/drivers/staging/mausb_host/src/hpal/data_common.c b/drivers/staging/mausb_host/src/hpal/data_common.c new file mode 100644 index 000000000000..032a84582dbe --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/data_common.c @@ -0,0 +1,139 @@ +// 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; + + 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; + + return mausb_send_data(dev, mausb_transfer_type_to_channel( + event->data.transfer_type), + &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 acknowledgement 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(dev, event, urb_ctx); + else + if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN) + status = mausb_receive_in_data(dev, event, urb_ctx); + else + status = mausb_receive_out_data(dev, event, urb_ctx); + +cleanup: + mausb_release_event_resources(event); + return status; +} diff --git a/drivers/staging/mausb_host/src/hpal/data_in.c b/drivers/staging/mausb_host/src/hpal/data_in.c new file mode 100644 index 000000000000..70d8fc02ef8a --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/data_in.c @@ -0,0 +1,82 @@ +// 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) +{ + int status = 0; + 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); + uint32_t kvec_num = setup_packet ? 2 : 1; + + 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; + + status = mausb_send_data(dev, mausb_transfer_type_to_channel( + event->data.transfer_type), + &data_to_send); + + return status; +} + +int mausb_receive_in_data(struct mausb_device *dev, 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; + uint16_t payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE; + uint32_t data_written = 0; + int status = 0; + + mausb_pr_debug(""); + + data_written = mausb_data_iterator_write(iterator, shift_ptr(common_hdr, + MAUSB_TRANSFER_HDR_SIZE), + 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); + } + + return status; +} diff --git a/drivers/staging/mausb_host/src/hpal/data_out.c b/drivers/staging/mausb_host/src/hpal/data_out.c new file mode 100644 index 000000000000..70a798da0f1b --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/data_out.c @@ -0,0 +1,207 @@ +// 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_out.h> + +#include <linux/slab.h> + +#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, + uint32_t num_of_data_chunks) +{ + struct mausb_payload_chunk *data_chunk = NULL, + *tmp = NULL; + uint32_t 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, + uint32_t *num_of_data_chunks) +{ + int status = mausb_add_data_chunk(common_hdr, + MAUSB_TRANSFER_HDR_SIZE, + chunks_list); + /* Success */ + if (!status) + ++(*num_of_data_chunks); + + return status; +} + +static int mausb_init_control_data_chunk(struct mausb_event *event, + struct list_head *chunks_list, + uint32_t *num_of_data_chunks) +{ + int status = 0; + + if (!event->data.first_control_packet) + goto l_return; + + status = mausb_add_data_chunk( + ((struct urb *)event->data.urb)->setup_packet, + MAUSB_CONTROL_SETUP_SIZE, chunks_list); + /* Success */ + if (!status) + ++(*num_of_data_chunks); + +l_return: + 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) +{ + uint32_t num_of_data_chunks = 0; + uint32_t num_of_payload_data_chunks = 0; + uint32_t payload_data_size = 0; + 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( + (struct ma_usb_hdr_common *)event->data.hdr, + &chunks_list, &num_of_data_chunks) < 0) { + status = -ENOMEM; + goto l_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 l_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 l_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 l_cleanup_data_chunks; + } + +l_cleanup_data_chunks: /* Cleanup all allocated data chunk in case of error */ + 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 = 0; + struct mausb_kvec_data_wrapper data; + + status = mausb_prepare_transfer_packet(&data, event, + &urb_ctx->iterator); + + if (status < 0) { + mausb_pr_err("Failed to prepare transfer packet"); + return status; + } + + status = mausb_send_data(dev, mausb_transfer_type_to_channel( + event->data.transfer_type), &data); + + kfree(data.kvec); + + return status; +} + +int mausb_receive_out_data(struct mausb_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx) +{ + struct urb *urb = urb_ctx->urb; + int status = 0; + + 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); + } + + return status; +} diff --git a/drivers/staging/mausb_host/src/hpal/hpal.c b/drivers/staging/mausb_host/src/hpal/hpal.c new file mode 100644 index 000000000000..9d76870623f0 --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/hpal.c @@ -0,0 +1,1327 @@ +// 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 <linux/kobject.h> +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/uio.h> +#include <linux/version.h> + +#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" + +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(uint8_t 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; + 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 (unlikely(!urb)) { + mausb_pr_err("Urb is NULL"); + *status = -EINVAL; + return NULL; + } + + urb_ctx = kzalloc(sizeof(*urb_ctx), GFP_ATOMIC); + + if (unlikely(!urb_ctx)) { + mausb_pr_alert("Urb_ctx allocation failed"); + *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 ret; + 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) { + ret = usb_hcd_link_urb_to_ep(urb->hcpriv, urb); + if (ret) { + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("Error %d while linking urb to hcd_endpoint", + ret); + kfree(urb_ctx); + return ret; + } + } + + 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, + 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 ret; + + if (!urb_ctx) + return false; + + spin_lock_irqsave(&mhcd->lock, flags); + if (link_urb_to_ep) { + ret = usb_hcd_link_urb_to_ep(urb_ctx->urb->hcpriv, + urb_ctx->urb); + if (ret) { + spin_unlock_irqrestore(&mhcd->lock, flags); + mausb_pr_err("Error %d while linking urb to hcd_endpoint", + ret); + 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 = 0; + + 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 = 0; + + 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 int32_t mausb_get_first_free_port_number(void) +{ + uint16_t 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 -1; + + mhcd->connected_ports |= (1 << port_number); + + return port_number; +} + +static inline void mausb_port_has_changed_event(struct mausb_device *dev, + struct mausb_event *event) +{ + int32_t port_number = 0; + unsigned long flags = 0; + + spin_lock_irqsave(&mhcd->lock, flags); + + port_number = mausb_get_first_free_port_number(); + + if (port_number < 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; + uint16_t i, + num_of_user_events, + num_of_completed_events; + unsigned long flags; + struct mausb_ring_buffer *mausb_ring = dev->ring_buffer; + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + num_of_completed_events = (uint16_t)dev->num_of_user_events; + num_of_user_events = dev->num_of_user_events >> + (8 * sizeof(num_of_user_events)); + dev->num_of_user_events = 0; + spin_unlock_irqrestore(&dev->num_of_user_events_lock, flags); + + status = mausb_ring_buffer_move_tail(mausb_ring, + num_of_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 < num_of_user_events; ++i) { + event = mausb_ring_current_from_user(mausb_ring); + mausb_ring_next_from_user(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 = 0; + + 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 mausb_completion mausb_completion; + int status; + + 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, + msecs_to_jiffies(3000)); + + mausb_pr_debug("Deleting MAUSB device event finished status %d", + status); + + mausb_remove_event(dev, &mausb_completion); + + status = wait_for_completion_interruptible_timeout( + &dev->user_finished_event, + msecs_to_jiffies(3000)); + mausb_pr_info("User event finished status=%d", 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, uint8_t 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 != NULL) { + 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 (unlikely(!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); + + 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, + uint8_t 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 != NULL) { + 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) +{ + int status = 0; + struct mausb_event mausb_event; + + mausb_pr_info(""); + + mausb_dev_reset_req_event(&mausb_event); + status = mausb_enqueue_event_to_user(dev, &mausb_event); + if (status < 0) { + mausb_pr_err("Ring buffer full, devreset failed"); + queue_work(dev->workq, &dev->hcd_disconnect_work); + return; + } +} + +int mausb_enqueue_event_from_user(uint8_t madev_addr, uint32_t all_events) +{ + unsigned long flags; + uint16_t num_of_completed, + num_of_events; + 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 0; + } + + spin_lock_irqsave(&dev->num_of_user_events_lock, flags); + num_of_completed = (uint16_t)all_events + + (uint16_t)dev->num_of_user_events; + num_of_events = (all_events >> (8 * sizeof(num_of_events))) + + (dev->num_of_user_events >> (8 * sizeof(num_of_events))); + dev->num_of_user_events = num_of_completed; + dev->num_of_user_events |= (uint32_t)num_of_events << + (8 * sizeof(num_of_events)); + 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 = 0; + + event->madev_addr = dev->madev_addr; + status = mausb_ring_buffer_put(dev->ring_buffer, event); + + if (unlikely(status < 0)) { + mausb_pr_err("Ring buffer operation failed"); + 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, uint16_t ep_handle, + struct urb *request) +{ + int status = 0; + struct mausb_event mausb_event; + + mausb_pr_debug(""); + + 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 = (uint64_t)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 = 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, uint32_t actual_length, int status) +{ + mausb_hcd_urb_complete(urb, actual_length, status); +} + +int mausb_signal_event(struct mausb_device *dev, + struct mausb_event *event, long 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 *endpoint_ctx; + struct mausb_device *ma_dev; + struct mausb_event mausb_event; + int status = 0; + + 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 urb=%p, ep_handle=%#x, dev_handle=%#x", + urb, endpoint_ctx->ep_handle, + endpoint_ctx->dev_handle); + goto l_complete_urb; + } + + mausb_pr_debug("urb=%p, ep_handle=%#x, dev_handle=%#x", + urb, endpoint_ctx->ep_handle, endpoint_ctx->dev_handle); + + if (!usb_endpoint_xfer_isoc(&urb->ep->desc)) { + status = mausb_canceltransfer_event_to_user( + endpoint_ctx->ma_dev, endpoint_ctx->dev_handle, + endpoint_ctx->ep_handle, (uint64_t)urb); + if (status < 0) { + mausb_pr_err("Failed to enqueue cancel transfer to user"); + goto l_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 = endpoint_ctx->ep_handle; + mausb_event.data.urb = (uint64_t)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(endpoint_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 l_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 l_complete_urb; + } + + return; + +l_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; + int status = 0; + + 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(); + status = wait_for_completion_timeout(&mss.client_stopped, + msecs_to_jiffies(MAUSB_CLIENT_STOPPED_TIMEOUT_MS)); + mausb_pr_info("Remaining time after waiting for stopping client %d", + status); +} + +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(uint8_t 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(uint8_t 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/staging/mausb_host/src/hpal/isoch_in.c b/drivers/staging/mausb_host/src/hpal/isoch_in.c new file mode 100644 index 000000000000..2c5e2bdf9871 --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/isoch_in.c @@ -0,0 +1,243 @@ +// 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 <linux/slab.h> +#include <linux/uio.h> + +#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) +{ + uint32_t i; + + if (urb->number_of_packets == 0) + return 0; + + isoch_readsize_block->service_intervals = urb->number_of_packets; + isoch_readsize_block->max_segment_length = + urb->iso_frame_desc[0].length; + + for (i = 0; i < urb->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) +{ + int status = 0; + uint32_t 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; + + 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 += 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 += 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 += kvec[2].iov_len; + data_to_send.kvec_num++; + } + + hdr->length = data_to_send.length; + data_to_send.kvec = kvec; + + status = mausb_send_data(dev, mausb_transfer_type_to_channel( + event->data.transfer_type), + &data_to_send); + + return status; +} + +static void __mausb_process_in_isoch_short_resp(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + struct mausb_urb_ctx *urb_ctx) +{ + uint8_t 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); + uint8_t *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers * + sizeof(*data_block_hdr)); + uint8_t *end_of_packet = shift_ptr(hdr, hdr->length); + struct urb *urb = urb_ctx->urb; + int i; + + if (unlikely(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) { + uint16_t seg_num = data_block_hdr[i].segment_number; + uint16_t seg_size = data_block_hdr[i].block_length; + + if (unlikely(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 (unlikely(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 (unlikely(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) +{ + uint8_t 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); + uint8_t *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers * + sizeof(struct ma_usb_hdr_isochdatablock_short)); + uint8_t *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) { + uint16_t seg_num = data_block_hdr[i].segment_number; + uint16_t seg_len = data_block_hdr[i].segment_length; + uint16_t block_len = data_block_hdr[i].block_length; + + if (unlikely(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 (unlikely(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 (unlikely(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/staging/mausb_host/src/hpal/isoch_out.c b/drivers/staging/mausb_host/src/hpal/isoch_out.c new file mode 100644 index 000000000000..74a14af4326a --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/isoch_out.c @@ -0,0 +1,362 @@ +// 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 <linux/slab.h> + +#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 struct ma_usb_hdr_common *__mausb_create_isoch_out_transfer_packet( + struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx, + uint16_t payload_size, uint32_t seq_n, + uint32_t start_of_segments, + uint32_t 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; + uint16_t length = 0; + uint16_t i; + unsigned long block_length; + uint32_t number_of_packets = (uint32_t)event->data.isoch_seg_num; + uint16_t size_of_request; + + size_of_request = MAUSB_ISOCH_TRANSFER_HDR_SIZE + + ISOCH_STANDARD_FORMAT_SIZE * number_of_segments; + + hdr = kzalloc(size_of_request, GFP_KERNEL); + if (unlikely(!hdr)) { + mausb_pr_alert("Header allocation failed"); + 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 = usb_endpoint_type(&urb->ep->desc) << 3; + + isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE); + + for (i = (uint16_t)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, + (uint64_t)ISOCH_STANDARD_FORMAT_SIZE * + (i - start_of_segments)); + isoc_header_std->block_length = (uint16_t)block_length; + isoc_header_std->segment_number = i; + isoc_header_std->s_flags = 0; + isoc_header_std->segment_length = (uint16_t)block_length; + isoc_header_std->fragment_offset = 0; + } + + length = (ISOCH_STANDARD_FORMAT_SIZE * number_of_segments) + + MAUSB_ISOCH_TRANSFER_HDR_SIZE; + + hdr->flags |= MA_USB_HDR_FLAGS_TIMESTAMP; + hdr->type = MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER); + hdr->data.headers = (uint16_t)number_of_segments; + hdr->data.i_flags = MA_USB_DATA_IFLAGS_HDR_FMT_STD | + MA_USB_DATA_IFLAGS_ASAP; + hdr_isochtransfer = mausb_get_isochtransfer_hdr(hdr); + hdr_opt_isochtransfer = mausb_hdr_isochtransfer_optional_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 = length + payload_size; + + return hdr; +} + +static int mausb_add_data_chunk(void *buffer, uint32_t buffer_size, + struct list_head *chunks_list) +{ + struct mausb_payload_chunk *data_chunk = NULL; + + data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL); + if (unlikely(!data_chunk)) { + mausb_pr_alert("Data chunk allocation failed"); + 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, + uint32_t *num_of_data_chunks, + uint32_t num_of_packets) +{ + uint32_t header_size = MAUSB_ISOCH_TRANSFER_HDR_SIZE + + ISOCH_STANDARD_FORMAT_SIZE * num_of_packets; + int status; + + status = mausb_add_data_chunk(common_hdr, header_size, chunks_list); + + /* Success */ + 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, + uint32_t num_of_data_chunks) +{ + struct mausb_payload_chunk *data_chunk = NULL, *tmp = NULL; + uint32_t 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) +{ + uint32_t num_of_data_chunks = 0; + uint32_t num_of_payload_data_chunks = 0; + uint32_t payload_data_size = 0; + uint32_t size_of_header = MAUSB_ISOCH_TRANSFER_HDR_SIZE + + ISOCH_STANDARD_FORMAT_SIZE * + event->data.isoch_seg_num; + 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, + event->data.isoch_seg_num) < 0){ + status = -ENOMEM; + goto l_cleanup_data_chunks; + } + + /* Get data chunks for data payload to send */ + INIT_LIST_HEAD(&payload_data_chunks); + payload_data_size = hdr->length - size_of_header; + + 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 l_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 l_cleanup_data_chunks; + } + +l_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, uint32_t *seq_n, + uint32_t payload_size, uint32_t start_of_segments, + uint32_t number_of_segments) +{ + struct ma_usb_hdr_common *hdr; + struct mausb_kvec_data_wrapper data_to_send; + int status; + + hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx, + 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; + } + + status = mausb_send_data(dev, mausb_transfer_type_to_channel( + event->data.transfer_type), + &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, uint32_t *seq_n, + uint32_t *starting_segments, uint32_t *rem_transfer_buf, + uint32_t *payload_size, int 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) +{ + uint32_t starting_segments = 0; + uint32_t rem_transfer_buf = MAX_ISOCH_ASAP_PACKET_SIZE; + struct urb *urb = (struct urb *)mausb_event->data.urb; + uint32_t number_of_packets = urb->number_of_packets; + uint32_t payload_size = 0; + uint32_t chunk_size = 0; + uint32_t seq_n = 0; + int status = 0; + int i = 0; + + 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 + 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 + + 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_device *dev, struct mausb_event *event, + struct mausb_urb_ctx *urb_ctx) +{ + struct urb *urb = (struct urb *)event->data.urb; + int status = 0; + uint16_t 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/staging/mausb_host/src/hpal/mausb_events.c b/drivers/staging/mausb_host/src/hpal/mausb_events.c new file mode 100644 index 000000000000..9b530d850321 --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/mausb_events.c @@ -0,0 +1,661 @@ +// 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/mausb_events.h> + +#include <linux/slab.h> + +#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 = ((hdr->ssid << 8) | hdr->dev_addr); + event->data.ep_handle = hdr->handle.epv; + event->data.recv_buf = (uint64_t) hdr; + + memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE); + + if (mausb_ctrl_transfer(hdr) && + hdr->length <= 2*MAUSB_TRANSFER_HDR_SIZE) { + memcpy(event->data.hdr_ack, + shift_ptr(hdr, MAUSB_TRANSFER_HDR_SIZE), + 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 = ((hdr->ssid << 8) | hdr->dev_addr); + event->data.ep_handle = hdr->handle.epv; + event->data.recv_buf = (uint64_t) hdr; + + memcpy(event->data.hdr, hdr, MAUSB_TRANSFER_HDR_SIZE); + + return 0; +} + +int mausb_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + mausb_pr_debug("channel=%d, type=%d", channel, hdr->type); + if (MA_USB_HDR_TYPE_IS_MANAGEMENT(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, long 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, long event_id, + struct mausb_device *dev) +{ + struct completion completion; + struct mausb_completion mausb_completion; + int status = 0; + + 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=%ld", event_id); + queue_work(dev->workq, &dev->hcd_disconnect_work); + return status; + } + + status = wait_for_completion_interruptible_timeout(&completion, + msecs_to_jiffies(MANAGEMENT_EVENT_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, + uint8_t device_speed, + uint32_t route_string, + uint16_t hub_dev_handle, + uint16_t parent_hs_hub_dev_handle, + uint16_t parent_hs_hub_port, uint16_t mtt, + uint8_t lse, int32_t *usb_dev_handle) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, + uint16_t descriptor_size, void *descriptor, + uint16_t *ep_handle) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, uint16_t ep_handle) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, uint16_t ep_handle) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, uint16_t ep_handle, + uint8_t tsp_flag) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, uint16_t ep_handle) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, uint16_t *ep_handle, + uint16_t max_packet_size) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, + uint16_t response_timeout) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, + uint16_t max_exit_latency, uint8_t hub, + uint8_t number_of_ports, uint8_t mtt, + uint8_t ttt, uint8_t integrated_hub_latency, + struct usb_device_descriptor *dev_descriptor) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t 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"); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + 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"); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + 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"); + queue_work(dev->workq, &dev->hcd_disconnect_work); + } + + return status; +} + +int mausb_usbdevreset_event_to_user(struct mausb_device *dev, + uint16_t device_handle) +{ + struct mausb_event event; + int status; + long 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=%ld", 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, + uint16_t device_handle, + uint16_t ep_handle, uint64_t urb) +{ + struct mausb_event event; + int status; + + event.type = MAUSB_EVENT_TYPE_CANCEL_TRANSFER; + event.mgmt.cancel_transfer.device_handle = device_handle; + event.mgmt.cancel_transfer.ep_handle = ep_handle; + event.mgmt.cancel_transfer.urb = urb; + event.madev_addr = dev->madev_addr; + + status = mausb_enqueue_event_to_user(dev, &event); + if (status < 0) { + mausb_pr_err("Ring buffer full, canceltransfer failed"); + queue_work(dev->workq, &dev->hcd_disconnect_work); + 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_pr_debug(""); + mausb_complete_urb(event); +} + +void mausb_cleanup_received_data_msg_event(struct mausb_event *event) +{ + mausb_pr_debug(""); + 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/staging/mausb_host/src/hpal/network_callbacks.c b/drivers/staging/mausb_host/src/hpal/network_callbacks.c new file mode 100644 index 000000000000..60d0dee0de0e --- /dev/null +++ b/drivers/staging/mausb_host/src/hpal/network_callbacks.c @@ -0,0 +1,162 @@ +// 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 <linux/uio.h> +#include <linux/slab.h> + +#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, + uint16_t 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) +{ + 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->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->dev_addr.Ip.Port.control, + MAUSB_CTRL_CHANNEL); + return; + } + } + + if (channel == MAUSB_CTRL_CHANNEL) { + if (dev->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->dev_addr.Ip.Port.bulk, + MAUSB_BULK_CHANNEL); + return; + } + } + + if (channel == MAUSB_BULK_CHANNEL) { + if (dev->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->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); + queue_work(dev->workq, &dev->socket_disconnect_work); + queue_work(dev->workq, &dev->hcd_disconnect_work); + return; + } +} + +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/staging/mausb_host/src/link/mausb_ip_link.c b/drivers/staging/mausb_host/src/link/mausb_ip_link.c new file mode 100644 index 000000000000..c20b118c4989 --- /dev/null +++ b/drivers/staging/mausb_host/src/link/mausb_ip_link.c @@ -0,0 +1,364 @@ +// 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 <linux/in.h> +#include <linux/inet.h> +#include <linux/jiffies.h> +#include <linux/net.h> +#include <linux/kernel.h> +#include <linux/skbuff.h> +#include <linux/socket.h> +#include <linux/version.h> +#include <linux/workqueue.h> +#include <net/tcp.h> +#include <net/sock.h> + +#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], + uint16_t 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 (unlikely(*ip_ctx == NULL)) { + mausb_pr_alert("ip context allocation failed"); + return -ENOMEM; + } + ctx = *ip_ctx; + ctx->client_socket = NULL; + __mausb_ip_recv_ctx_clear(&ctx->recv_ctx); + /* something safer than strcpy */ + 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) +{ + uint32_t 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("Conected 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) +{ + int status = 0; + + if (ip_ctx && ip_ctx->client_socket) + status = kernel_sock_shutdown(ip_ctx->client_socket, SHUT_RDWR); + return status; +} + +int mausb_ip_send(struct mausb_ip_ctx *ip_ctx, + struct mausb_kvec_data_wrapper *wrapper) +{ + struct msghdr msghd; + int status; + + if (!ip_ctx) { + mausb_pr_alert("Socket ctx is NULL!"); + return -EINVAL; + } + + memset(&msghd, 0, sizeof(msghd)); + msghd.msg_flags = MSG_WAITALL; + + status = kernel_sendmsg(ip_ctx->client_socket, &msghd, wrapper->kvec, + wrapper->kvec_num, wrapper->length); + + return status; +} + +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; + + /* 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( + (struct socket *)ip_ctx->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((struct socket *)ip_ctx->client_socket, + &msghd, &vec, 1, vec.iov_len, + msghd.msg_flags); + + if (status == -EAGAIN) { + return status; + } 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; + + } else { + mausb_pr_debug("kernel_recvmsg host %s:%d, status=%d", + ip_ctx->ip_addr, ip_ctx->port, status); + } + + if (peek) { + if (status < sizeof(ip_ctx->recv_ctx.common_hdr)) + return -EAGAIN; + /* length field of mausb_common_hdr */ + ip_ctx->recv_ctx.left = + *(uint16_t *) (&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 -= status; + ip_ctx->recv_ctx.received += 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/staging/mausb_host/src/utils/mausb_data_iterator.c b/drivers/staging/mausb_host/src/utils/mausb_data_iterator.c new file mode 100644 index 000000000000..3b97342bb1f3 --- /dev/null +++ b/drivers/staging/mausb_host/src/utils/mausb_data_iterator.c @@ -0,0 +1,300 @@ +// 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 <linux/slab.h> +#include <linux/string.h> +#include <linux/uio.h> + +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" + +static int mausb_read_virtual_buffer(struct mausb_data_iter *iterator, + uint32_t byte_num, + struct list_head *data_chunks_list, + uint32_t *data_chunks_num) +{ + uint32_t rem_data = 0; + uint32_t 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 = (uint8_t *) (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, + uint32_t byte_num, + struct list_head *data_chunks_list, + uint32_t *data_chunks_num) +{ + uint32_t current_sg_read_num = 0; + 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 = (uint8_t *) 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 uint32_t mausb_write_virtual_buffer(struct mausb_data_iter *iterator, + void *buffer, uint32_t size) +{ + + uint32_t rem_space = 0; + uint32_t 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 uint32_t mausb_write_scatterlist_buffer(struct mausb_data_iter *iterator, + void *buffer, uint32_t size) +{ + uint32_t current_sg_rem_space = 0; + uint32_t count = 0; + uint32_t 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 = 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, + uint32_t byte_num, + struct list_head *data_chunks_list, + uint32_t *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); + +} + +uint32_t mausb_data_iterator_write(struct mausb_data_iter *iterator, + void *buffer, uint32_t 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, + uint32_t seek_delta) +{ + iterator->offset += min(seek_delta, iterator->length - + iterator->offset); +} + +static void mausb_seek_scatterlist_buffer(struct mausb_data_iter *iterator, + uint32_t seek_delta) +{ + uint32_t rem_bytes_in_current_scatter = 0; + + while (seek_delta) { + rem_bytes_in_current_scatter = + 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, + uint32_t 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 += 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, + uint32_t buffer_len, struct scatterlist *sg, + uint32_t 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; + } + +} + +uint32_t mausb_data_iterator_length(struct mausb_data_iter *iterator) +{ + return iterator->length; +} diff --git a/drivers/staging/mausb_host/src/utils/mausb_mmap.c b/drivers/staging/mausb_host/src/utils/mausb_mmap.c new file mode 100644 index 000000000000..3beb160b9e72 --- /dev/null +++ b/drivers/staging/mausb_host/src/utils/mausb_mmap.c @@ -0,0 +1,358 @@ +// 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 <linux/atomic.h> +#include <linux/cdev.h> +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/version.h> + +#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 int mausb_major_kernel = -1; +static struct cdev mausb_kernel_dev; +static struct class *mausb_kernel_class; + +static void mausb_vm_open(struct vm_area_struct *vma) +{ + mausb_pr_info(""); +} + +static void mausb_vm_close(struct vm_area_struct *vma) +{ + struct mausb_ring_buffer *buffer = NULL, *next = NULL; + unsigned long flags = 0; + uint64_t ring_buffer_id = *(uint64_t *)(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_info(""); + 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) +{ + mausb_pr_info(""); + + filp->private_data = NULL; + + return 0; +} + +static int mausb_file_close(struct inode *inode, struct file *filp) +{ + mausb_pr_info(""); + + 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 = 0; + int completed_events = 0; + int ring_events = 0; + struct mausb_ring_buffer *ring_buffer; + struct mausb_device *dev = NULL; + unsigned long flags = 0; + uint8_t current_device = 0; + int status = 0; + int8_t fail_ret_val = 0; + + /* Reset heartbeat timer events */ + mausb_reset_heartbeat_cnt(); + + if (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; + } + + status = wait_for_completion_interruptible_timeout( + &mss.rings_events.mausb_ring_has_events, + msecs_to_jiffies(MAUSB_READ_DEVICE_TIMEOUT_MS)); + + reinit_completion(&mss.rings_events.mausb_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, + ring_events); + completed_events = atomic_xchg( + &ring_buffer->mausb_completed_user_events, + completed_events); + mss.events[current_device].num_of_events = ring_events; + mss.events[current_device].num_of_completed_events = + completed_events; + completed_events = 0; + ring_events = 0; + if (++current_device == MAUSB_MAX_NUM_OF_MA_DEVS) + break; + } + + spin_unlock_irqrestore(&mss.lock, flags); + + num_of_bytes_to_read = current_device * + sizeof(struct mausb_events_notification); + + num_of_bytes_not_copied = copy_to_user(user_buffer, &mss.events, + 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 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; + int status = 0; + unsigned long flags = 0; + struct mausb_ring_buffer *ring_buffer = kzalloc(sizeof(*ring_buffer), + GFP_KERNEL); + + if (!ring_buffer) + return -ENOMEM; + + status = mausb_ring_buffer_init(ring_buffer); + if (status < 0) { + mausb_pr_err("Ring buffer init failed"); + mausb_ring_buffer_destroy(ring_buffer); + kfree(ring_buffer); + return -ENOMEM; + } + + vma->vm_private_data = kzalloc(sizeof(ring_buffer->id), GFP_KERNEL); + if (!vma->vm_private_data) { + mausb_pr_err("Failed to allocate private data"); + mausb_ring_buffer_destroy(ring_buffer); + kfree(ring_buffer); + return -ENOMEM; + } + + filp->private_data = vma->vm_private_data; + + mausb_pr_debug(""); + vma->vm_ops = &mausb_vm_ops; + mausb_vm_open(vma); + + page = virt_to_page(ring_buffer->to_user_buffer); + + if (size > (2 * MAUSB_RING_BUFFER_SIZE * sizeof(struct mausb_event))) { + mausb_pr_err("Invalid memory size to map"); + mausb_ring_buffer_destroy(ring_buffer); + kfree(ring_buffer); + return -EINVAL; + } + + 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"); + mausb_ring_buffer_destroy(ring_buffer); + kfree(ring_buffer); + return ret; + } + + spin_lock_irqsave(&mss.lock, flags); + ring_buffer->id = mss.ring_buffer_id++; + *(uint64_t *)(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; +} + +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, + .mmap = mausb_mmap, +}; + +int mausb_create_dev(void) +{ + int device_created = 0; + int status = 0; + + mausb_pr_info(""); + + /* cat /proc/devices */ + status = alloc_chrdev_region(&mausb_major_kernel, 0, 1, + MAUSB_KERNEL_DEV_NAME "_proc"); + if (status) + goto cleanup; + /* ls /sys/class */ + 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) +{ + mausb_pr_info("Enter"); + + 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"); + } + if (mausb_major_kernel != -1) { + unregister_chrdev_region(mausb_major_kernel, 1); + mausb_pr_info("unregistered"); + } + mausb_pr_info("Exit"); +} + +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) +{ + mausb_pr_info(""); + atomic_set(&mss.rings_events.mausb_stop_reading_ring_events, 1); + complete(&mss.rings_events.mausb_ring_has_events); +} diff --git a/drivers/staging/mausb_host/src/utils/mausb_ring_buffer.c b/drivers/staging/mausb_host/src/utils/mausb_ring_buffer.c new file mode 100644 index 000000000000..5abd396b07b5 --- /dev/null +++ b/drivers/staging/mausb_host/src/utils/mausb_ring_buffer.c @@ -0,0 +1,140 @@ +// 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 <linux/cdev.h> +#include <linux/circ_buf.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/log2.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/version.h> + +#include "hpal/mausb_events.h" +#include "utils/mausb_logs.h" + +static 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; + } +} + +static 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 = ilog2(num_of_pages) + + (is_power_of_2(num_of_pages) ? 0 : 1); + return order; +} + +int mausb_ring_buffer_init(struct mausb_ring_buffer *ring) +{ + 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; + spin_lock_init(&ring->lock); + + return 0; +} + +int mausb_ring_buffer_put(struct mausb_ring_buffer *ring, + struct mausb_event *event) +{ + unsigned long flags; + + spin_lock_irqsave(&ring->lock, flags); + if (CIRC_SPACE(ring->head, ring->tail, 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; +} + +static int mausb_ring_buffer_get(struct mausb_ring_buffer *ring, + struct mausb_event *event) +{ + unsigned long flags; + + 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; +} + +void mausb_ring_buffer_cleanup(struct mausb_ring_buffer *ring) +{ + struct mausb_event event; + + mausb_pr_info(""); + while (mausb_ring_buffer_get(ring, &event) == 0) + mausb_cleanup_ring_buffer_event(&event); +} + +void mausb_ring_buffer_destroy(struct mausb_ring_buffer *ring) +{ + 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); +} + +int mausb_ring_buffer_move_tail(struct mausb_ring_buffer *ring, uint32_t count) +{ + unsigned long flags; + + 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 + 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; +}
_______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel