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 +#else +#include +#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 +#else +#include +#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 + +#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 +#include + +#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 +#include +#include +#include +#include +#include +#include + +#include "utils/mausb_ring_buffer.h" + +#define DEVICE_NAME "mausb_host_hcd_dev" +#define CLASS_NAME "mausb" + +#define NUMBER_OF_PORTS 15 + +#define MAX_USB_DEVICE_DEPTH 6 + +#define RESPONSE_TIMEOUT 5000 + +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 + +#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 +#include +#include + +#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 + +#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 +#include +#include +#include + +#define MAUSB_LINK_BUFF_SIZE 16777216 +#define MAUSB_LINK_TOS_LEVEL_EF 0xB8 + +enum mausb_link_action { + MAUSB_LINK_CONNECT = 0, + MAUSB_LINK_DISCONNECT = 1, + MAUSB_LINK_RECV = 2, + MAUSB_LINK_SEND = 3 +}; + +enum mausb_channel { + MAUSB_CTRL_CHANNEL = 0, + MAUSB_ISOCH_CHANNEL = 1, + MAUSB_BULK_CHANNEL = 2, + MAUSB_INTR_CHANNEL = 3, + MAUSB_MGMT_CHANNEL = 4 +}; + +struct mausb_kvec_data_wrapper { + struct kvec *kvec; + 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 +#include +#include + +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 +#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 + +#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 + +#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 +#include +#include +#include +#include +#include +#include +#include + +#include "common/mausb_address.h" +#include "hcd/vhcd.h" +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_mmap.h" +#include "utils/mausb_version.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("DisplayLink (UK) Ltd."); +MODULE_VERSION(MAUSB_DRIVER_VERSION); + +static struct mausb_device_address device_address; +static int mausb_device_disconnect_param; +static 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 +#include +#include + +#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 + +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_logs.h" + +static int mausb_add_data_chunk(void *buffer, u32 buffer_size, + struct list_head *chunks_list) +{ + struct mausb_payload_chunk *data_chunk; + + data_chunk = kzalloc(sizeof(*data_chunk), GFP_KERNEL); + if (!data_chunk) + return -ENOMEM; + + /* Initialize data chunk for MAUSB header and add it to chunks list */ + INIT_LIST_HEAD(&data_chunk->list_entry); + + data_chunk->kvec.iov_base = buffer; + data_chunk->kvec.iov_len = buffer_size; + list_add_tail(&data_chunk->list_entry, chunks_list); + return 0; +} + +static int mausb_init_data_wrapper(struct mausb_kvec_data_wrapper *data, + struct list_head *chunks_list, + 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 +#include +#include +#include +#include + +#include "common/mausb_event.h" +#include "hcd/hub.h" +#include "hcd/vhcd.h" +#include "hpal/mausb_events.h" +#include "hpal/data_common.h" +#include "hpal/network_callbacks.h" +#include "link/mausb_ip_link.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_mmap.h" +#include "utils/mausb_ring_buffer.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_version.h" + +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 +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "hpal/hpal.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_data_iterator.h" +#include "utils/mausb_logs.h" + +static inline u32 __mausb_isoch_prepare_read_size_block( + struct ma_usb_hdr_isochreadsizeblock_std *isoch_readsize_block, + struct urb *urb) +{ + 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 + +#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 + +#include + +#include "hcd/hub.h" +#include "hpal/data_common.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_data_iterator.h" + +void mausb_dev_reset_req_event(struct mausb_event *event) +{ + event->type = MAUSB_EVENT_TYPE_DEV_RESET; +} + +static int mausb_mgmt_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + int status = 0; + + mausb_pr_info("channel=%d, type=%d", channel, hdr->type); + if (hdr->length <= MAUSB_MAX_MGMT_SIZE) { + event->type = MAUSB_EVENT_TYPE_RECEIVED_MGMT_MSG; + memcpy(event->mgmt.mgmt_hdr.hdr, hdr, hdr->length); + } else { + mausb_pr_err("MGMT message to long, failed to copy"); + status = -EINVAL; + } + + kfree(hdr); + return status; +} + +static int mausb_data_msg_received_event(struct mausb_event *event, + struct ma_usb_hdr_common *hdr, + enum mausb_channel channel) +{ + event->type = MAUSB_EVENT_TYPE_RECEIVED_DATA_MSG; + event->data.transfer_type = mausb_transfer_type_from_hdr(hdr); + event->data.device_id = ((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 +#include + +#include "hpal/hpal.h" +#include "hpal/mausb_events.h" +#include "utils/mausb_logs.h" + +static void mausb_init_ip_ctx_helper(struct mausb_device *dev, + struct mausb_ip_ctx **ip_ctx, + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils/mausb_logs.h" + +static void __mausb_ip_connect(struct work_struct *work); +static int __mausb_ip_recv(struct mausb_ip_ctx *ip_ctx); +static void __mausb_ip_recv_work(struct work_struct *work); +static inline void __mausb_ip_recv_ctx_clear(struct mausb_ip_recv_ctx + *recv_ctx); +static inline void __mausb_ip_recv_ctx_free(struct mausb_ip_recv_ctx + *recv_ctx); + +int mausb_init_ip_ctx(struct mausb_ip_ctx **ip_ctx, + struct net *net_ns, + char ip_addr[INET6_ADDRSTRLEN], + 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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/mausb_driver_status.h" +#include "hpal/hpal.h" +#include "utils/mausb_logs.h" +#include "utils/mausb_ring_buffer.h" + +#define MAUSB_KERNEL_DEV_NAME "mausb_host" +#define MAUSB_READ_DEVICE_TIMEOUT_MS 500 + +static 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +}