[PATCH v4 8/8] usb: mausb_host: Process MA-USB data packets

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Added implementation of MA-USB data&isoch packets processing logic,
both for IN and OUT directions.

Signed-off-by: Vladimir Stankovic <vladimir.stankovic@xxxxxxxxxxxxxxx>
---
 drivers/usb/mausb_host/Makefile    |   1 +
 drivers/usb/mausb_host/hpal.c      |  32 +-
 drivers/usb/mausb_host/hpal_data.c | 719 +++++++++++++++++++++++++++++
 drivers/usb/mausb_host/hpal_data.h |  34 ++
 4 files changed, 784 insertions(+), 2 deletions(-)
 create mode 100644 drivers/usb/mausb_host/hpal_data.c
 create mode 100644 drivers/usb/mausb_host/hpal_data.h

diff --git a/drivers/usb/mausb_host/Makefile b/drivers/usb/mausb_host/Makefile
index fd2a36a04ad6..a5fd033c002e 100644
--- a/drivers/usb/mausb_host/Makefile
+++ b/drivers/usb/mausb_host/Makefile
@@ -12,5 +12,6 @@ mausb_host-y += ip_link.o
 mausb_host-y += hcd.o
 mausb_host-y += hpal.o
 mausb_host-y += hpal_events.o
+mausb_host-y += hpal_data.o
 
 ccflags-y += -I$(srctree)/$(src)
diff --git a/drivers/usb/mausb_host/hpal.c b/drivers/usb/mausb_host/hpal.c
index 1e7bbe3b230a..ecbb8e6d6f84 100644
--- a/drivers/usb/mausb_host/hpal.c
+++ b/drivers/usb/mausb_host/hpal.c
@@ -11,6 +11,7 @@
 #include <linux/uio.h>
 
 #include "hcd.h"
+#include "hpal_data.h"
 #include "hpal_events.h"
 #include "utils.h"
 
@@ -1387,6 +1388,7 @@ 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)
 {
 	struct mausb_urb_ctx *urb_ctx;
+	int status = 0;
 
 	if (event->status != 0) {
 		mausb_pr_err("Event %d failed with status %d",
@@ -1401,9 +1403,22 @@ int mausb_send_data_msg(struct mausb_device *dev, struct mausb_event *event)
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled for event=%d",
 			      event->type);
+		return status;
 	}
 
-	return 0;
+	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)
@@ -1426,6 +1441,20 @@ int mausb_receive_data_msg(struct mausb_device *dev, struct mausb_event *event)
 	if (!urb_ctx) {
 		/* Transfer will be deleted from dequeue task */
 		mausb_pr_warn("Urb is already cancelled");
+		goto cleanup;
+	}
+
+	if (mausb_isoch_data_event(event)) {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			status = mausb_receive_isoch_in_data(dev, event,
+							     urb_ctx);
+		else
+			status = mausb_receive_isoch_out(event);
+	} else {
+		if (event->data.direction == MAUSB_DATA_MSG_DIRECTION_IN)
+			mausb_receive_in_data(event, urb_ctx);
+		else
+			mausb_receive_out_data(event, urb_ctx);
 	}
 
 cleanup:
@@ -1593,7 +1622,6 @@ static void mausb_handle_receive_event(struct mausb_device *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);
 
diff --git a/drivers/usb/mausb_host/hpal_data.c b/drivers/usb/mausb_host/hpal_data.c
new file mode 100644
index 000000000000..bf076418e596
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_data.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#include "hpal_data.h"
+
+#include <linux/slab.h>
+#include <linux/uio.h>
+
+#include "hcd.h"
+#include "hpal.h"
+#include "hpal_events.h"
+#include "utils.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[2];
+	struct urb *urb   = (struct urb *)(event->data.urb);
+	bool setup_packet = (usb_endpoint_xfer_control(&urb->ep->desc) &&
+			     urb->setup_packet);
+	u32 kvec_num = setup_packet ? 2 : 1;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= kvec_num;
+	data_to_send.length	= MAUSB_TRANSFER_HDR_SIZE +
+			(setup_packet ? MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base = event->data.hdr;
+	kvec[0].iov_len  = MAUSB_TRANSFER_HDR_SIZE;
+
+	/* Prepare setup packet kvec */
+	if (setup_packet) {
+		kvec[1].iov_base = urb->setup_packet;
+		kvec[1].iov_len  = MAUSB_CONTROL_SETUP_SIZE;
+	}
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+	struct mausb_data_iter *iterator     = &urb_ctx->iterator;
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	void *buffer;
+	u32 payload_size = common_hdr->length - MAUSB_TRANSFER_HDR_SIZE;
+	u32 data_written = 0;
+
+	buffer = shift_ptr(common_hdr, MAUSB_TRANSFER_HDR_SIZE);
+	data_written = mausb_data_iterator_write(iterator, buffer,
+						 payload_size);
+
+	mausb_pr_debug("data_written=%d, payload_size=%d", data_written,
+		       payload_size);
+	event->data.rem_transfer_size -= data_written;
+
+	if (event->data.transfer_eot) {
+		mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+			       event->data.transfer_size,
+			       event->data.rem_transfer_size, event->status);
+		mausb_complete_request(urb, event->data.transfer_size -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static int
+mausb_init_data_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				 struct list_head *chunks_list,
+				 u32 *num_of_data_chunks)
+{
+	int status = mausb_add_data_chunk(common_hdr, MAUSB_TRANSFER_HDR_SIZE,
+					  chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int mausb_init_control_data_chunk(struct mausb_event *event,
+					 struct list_head *chunks_list,
+					 u32 *num_of_data_chunks)
+{
+	int status;
+	void *buffer = ((struct urb *)event->data.urb)->setup_packet;
+
+	if (!event->data.first_control_packet)
+		return 0;
+
+	status = mausb_add_data_chunk(buffer, MAUSB_CONTROL_SETUP_SIZE,
+				      chunks_list);
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static int
+mausb_prepare_transfer_packet(struct mausb_kvec_data_wrapper *wrapper,
+			      struct mausb_event *event,
+			      struct mausb_data_iter *iterator)
+{
+	u32 num_of_data_chunks		= 0;
+	u32 num_of_payload_data_chunks	= 0;
+	u32 payload_data_size		= 0;
+	int status = 0;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	struct ma_usb_hdr_common *data_hdr = (struct ma_usb_hdr_common *)
+			event->data.hdr;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_data_out_header_chunk(data_hdr, &chunks_list,
+					     &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/*
+	 * Initialize data chunk for MAUSB control setup packet and
+	 * add it to chunks list
+	 */
+	if (mausb_init_control_data_chunk(event, &chunks_list,
+					  &num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size =
+			((struct ma_usb_hdr_common *)event->data.hdr)->length -
+			 MAUSB_TRANSFER_HDR_SIZE -
+			 (event->data.first_control_packet ?
+			  MAUSB_CONTROL_SETUP_SIZE : 0);
+
+	if (mausb_data_iterator_read(iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks: /* Cleanup all allocated data chunks */
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	int status;
+	struct mausb_kvec_data_wrapper data;
+	enum mausb_channel channel;
+
+	status = mausb_prepare_transfer_packet(&data, event,
+					       &urb_ctx->iterator);
+
+	if (status < 0) {
+		mausb_pr_err("Failed to prepare transfer packet");
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data);
+
+	kfree(data.kvec);
+
+	return status;
+}
+
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx)
+{
+	struct urb *urb = urb_ctx->urb;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size, event->data.rem_transfer_size,
+		       event->status);
+
+	if (event->data.transfer_eot) {
+		mausb_complete_request(urb, urb->transfer_buffer_length -
+				       event->data.rem_transfer_size,
+				       event->status);
+	}
+}
+
+static inline u32
+__mausb_isoch_prepare_read_size_block(struct ma_usb_hdr_isochreadsizeblock_std *
+				      isoch_readsize_block, struct urb *urb)
+{
+	u32 i;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+
+	if (number_of_packets == 0)
+		return 0;
+
+	isoch_readsize_block->service_intervals  = number_of_packets;
+	isoch_readsize_block->max_segment_length =
+					(u32)urb->iso_frame_desc[0].length;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		urb->iso_frame_desc[i].status = 0;
+		urb->iso_frame_desc[i].actual_length = 0;
+	}
+
+	return sizeof(struct ma_usb_hdr_isochreadsizeblock_std);
+}
+
+int mausb_send_isoch_in_msg(struct mausb_device *dev, struct mausb_event *event)
+{
+	u32 read_size_block_length = 0;
+	struct mausb_kvec_data_wrapper data_to_send;
+	struct kvec kvec[MAUSB_ISOCH_IN_KVEC_NUM];
+	struct ma_usb_hdr_isochtransfer_optional opt_isoch_hdr;
+	struct ma_usb_hdr_isochreadsizeblock_std isoch_readsize_block;
+	struct ma_usb_hdr_common *hdr =
+				(struct ma_usb_hdr_common *)event->data.hdr;
+	struct urb *urb = (struct urb *)event->data.urb;
+	enum mausb_channel channel;
+
+	data_to_send.kvec_num	= 0;
+	data_to_send.length	= 0;
+
+	/* Prepare transfer header kvec */
+	kvec[0].iov_base     = event->data.hdr;
+	kvec[0].iov_len	     = MAUSB_TRANSFER_HDR_SIZE;
+	data_to_send.length += (u32)kvec[0].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare optional header kvec */
+	opt_isoch_hdr.timestamp = MA_USB_TRANSFER_RESERVED;
+	opt_isoch_hdr.mtd	= MA_USB_TRANSFER_RESERVED;
+
+	kvec[1].iov_base     = &opt_isoch_hdr;
+	kvec[1].iov_len	     = sizeof(struct ma_usb_hdr_isochtransfer_optional);
+	data_to_send.length += (u32)kvec[1].iov_len;
+	data_to_send.kvec_num++;
+
+	/* Prepare read size blocks */
+	read_size_block_length =
+		__mausb_isoch_prepare_read_size_block(&isoch_readsize_block,
+						      urb);
+	if (read_size_block_length > 0) {
+		kvec[2].iov_base     = &isoch_readsize_block;
+		kvec[2].iov_len	     = read_size_block_length;
+		data_to_send.length += (u32)kvec[2].iov_len;
+		data_to_send.kvec_num++;
+	}
+
+	hdr->length = (u16)data_to_send.length;
+	data_to_send.kvec = kvec;
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	return mausb_send_data(dev, channel, &data_to_send);
+}
+
+static void __mausb_process_in_isoch_short_resp(struct mausb_event *event,
+						struct ma_usb_hdr_common *hdr,
+						struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_short *data_block_hdr =
+			(struct ma_usb_hdr_isochdatablock_short *)
+			shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+				  opt_hdr_shift);
+	u8 *isoch_data = shift_ptr(data_block_hdr, hdr->data.headers *
+				   sizeof(*data_block_hdr));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = urb_ctx->urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			     event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num  = data_block_hdr[i].segment_number;
+		u16 seg_size = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, urb.number_of_packets=%d",
+				     event->data.ep_handle, seg_num,
+				     urb->number_of_packets);
+			break;
+		}
+
+		if (seg_size > urb->iso_frame_desc[seg_num].length) {
+			mausb_pr_err("Block to long for segment: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, seg_size) > end_of_packet) {
+			mausb_pr_err("End of segment after enf of packet: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset);
+		mausb_data_iterator_write(&urb_ctx->iterator, isoch_data,
+					  seg_size);
+
+		isoch_data = shift_ptr(isoch_data, seg_size);
+
+		urb->iso_frame_desc[seg_num].actual_length = seg_size;
+		urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+static void __mausb_process_in_isoch_std_resp(struct mausb_event *event,
+					      struct ma_usb_hdr_common *hdr,
+					      struct mausb_urb_ctx *urb_ctx)
+{
+	u8 opt_hdr_shift = (hdr->flags & MA_USB_HDR_FLAGS_TIMESTAMP) ?
+			   sizeof(struct ma_usb_hdr_isochtransfer_optional) : 0;
+	struct ma_usb_hdr_isochdatablock_std *data_block_hdr =
+		(struct ma_usb_hdr_isochdatablock_std *)
+		shift_ptr(mausb_hdr_isochtransfer_optional_hdr(hdr),
+			  opt_hdr_shift);
+	u8 *isoch_data =
+		shift_ptr(data_block_hdr, hdr->data.headers *
+			  sizeof(struct ma_usb_hdr_isochdatablock_std));
+	u8 *end_of_packet = shift_ptr(hdr, hdr->length);
+	struct urb *urb = (struct urb *)event->data.urb;
+	int i;
+
+	if (isoch_data >= end_of_packet) {
+		mausb_pr_err("Bad header data. Data start pointer after end of packet: ep_handle=%#x",
+			     event->data.ep_handle);
+		return;
+	}
+
+	for (i = 0; i < hdr->data.headers; ++i) {
+		u16 seg_num   = data_block_hdr[i].segment_number;
+		u16 seg_len   = data_block_hdr[i].segment_length;
+		u16 block_len = data_block_hdr[i].block_length;
+
+		if (seg_num >= urb->number_of_packets) {
+			mausb_pr_err("Too many segments: ep_handle=%#x, seg_num=%d, number_of_packets=%d",
+				     event->data.ep_handle, seg_num,
+				     urb->number_of_packets);
+			break;
+		}
+
+		if (block_len > urb->iso_frame_desc[seg_num].length -
+			     urb->iso_frame_desc[seg_num].actual_length) {
+			mausb_pr_err("Block too long for segment: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		if (shift_ptr(isoch_data, block_len) >
+				       end_of_packet) {
+			mausb_pr_err("End of fragment after end of packet: ep_handle=%#x",
+				     event->data.ep_handle);
+			break;
+		}
+
+		mausb_reset_data_iterator(&urb_ctx->iterator);
+		mausb_data_iterator_seek(&urb_ctx->iterator,
+					 urb->iso_frame_desc[seg_num].offset +
+					 data_block_hdr[i].fragment_offset);
+		mausb_data_iterator_write(&urb_ctx->iterator,
+					  isoch_data, block_len);
+		isoch_data = shift_ptr(isoch_data, block_len);
+
+		urb->iso_frame_desc[seg_num].actual_length += block_len;
+
+		if (urb->iso_frame_desc[seg_num].actual_length == seg_len)
+			urb->iso_frame_desc[seg_num].status = 0;
+	}
+}
+
+int mausb_receive_isoch_in_data(struct mausb_device *dev,
+				struct mausb_event *event,
+				struct mausb_urb_ctx *urb_ctx)
+{
+	struct ma_usb_hdr_common *common_hdr =
+			(struct ma_usb_hdr_common *)event->data.recv_buf;
+	struct ma_usb_hdr_transfer *transfer_hdr =
+					mausb_get_data_transfer_hdr(common_hdr);
+
+	if (!(common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK)) {
+		/* Short ISO headers response */
+		__mausb_process_in_isoch_short_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_STD) {
+		/* Standard ISO headers response */
+		__mausb_process_in_isoch_std_resp(event, common_hdr, urb_ctx);
+	} else if ((common_hdr->data.i_flags & MA_USB_DATA_IFLAGS_FMT_MASK) &
+		MA_USB_DATA_IFLAGS_HDR_FMT_LONG) {
+		/* Long ISO headers response */
+		mausb_pr_warn("Long isoc headers in response: ep_handle=%#x, req_id=%#x",
+			      event->data.ep_handle, transfer_hdr->req_id);
+	} else {
+		/* Error */
+		mausb_pr_err("Isoc header error in response: ep_handle=%#x, req_id=%#x",
+			     event->data.ep_handle, transfer_hdr->req_id);
+	}
+
+	return 0;
+}
+
+static inline u32
+__mausb_calculate_isoch_common_header_size(u32 num_of_segments)
+{
+	return MAUSB_ISOCH_TRANSFER_HDR_SIZE +
+			MAUSB_ISOCH_STANDARD_FORMAT_SIZE * num_of_segments;
+}
+
+static struct ma_usb_hdr_common *
+__mausb_create_isoch_out_transfer_packet(struct mausb_event *event,
+					 struct mausb_urb_ctx *urb_ctx,
+					 u16 payload_size, u32 seq_n,
+					 u32 start_of_segments,
+					 u32 number_of_segments)
+{
+	struct ma_usb_hdr_common		 *hdr;
+	struct ma_usb_hdr_isochtransfer		 *hdr_isochtransfer;
+	struct ma_usb_hdr_isochdatablock_std	 *isoc_header_std;
+	struct ma_usb_hdr_isochtransfer_optional *hdr_opt_isochtransfer;
+	struct urb *urb = (struct urb *)event->data.urb;
+	void *isoc_headers = NULL;
+	u32 length;
+	u16 i;
+	unsigned long block_length;
+	u32 number_of_packets = (u32)event->data.isoch_seg_num;
+	u32 size_of_request =
+		__mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr = kzalloc(size_of_request, GFP_KERNEL);
+	if (!hdr)
+		return NULL;
+
+	hdr->version	  = MA_USB_HDR_VERSION_1_0;
+	hdr->ssid	  = event->data.mausb_ssid;
+	hdr->flags	  = MA_USB_HDR_FLAGS_HOST;
+	hdr->dev_addr	  = event->data.mausb_address;
+	hdr->handle.epv	  = event->data.ep_handle;
+	hdr->data.status  = MA_USB_HDR_STATUS_NO_ERROR;
+	hdr->data.eps	  = MAUSB_TRANSFER_RESERVED;
+	hdr->data.t_flags = (u8)(usb_endpoint_type(&urb->ep->desc) << 3);
+
+	isoc_headers = shift_ptr(hdr, MAUSB_ISOCH_TRANSFER_HDR_SIZE);
+
+	for (i = (u16)start_of_segments;
+	     i < number_of_segments + start_of_segments; ++i) {
+		block_length = i < number_of_packets - 1 ?
+			urb->iso_frame_desc[i + 1].offset -
+			urb->iso_frame_desc[i].offset :
+			mausb_data_iterator_length(&urb_ctx->iterator) -
+			urb->iso_frame_desc[i].offset;
+
+		urb->iso_frame_desc[i].status = MA_USB_HDR_STATUS_UNSUCCESSFUL;
+		isoc_header_std = (struct ma_usb_hdr_isochdatablock_std *)
+			shift_ptr(isoc_headers,
+				  (u64)MAUSB_ISOCH_STANDARD_FORMAT_SIZE *
+				  (i - start_of_segments));
+		isoc_header_std->block_length	 = (u16)block_length;
+		isoc_header_std->segment_number  = i;
+		isoc_header_std->s_flags	 = 0;
+		isoc_header_std->segment_length  = (u16)block_length;
+		isoc_header_std->fragment_offset = 0;
+	}
+
+	length = __mausb_calculate_isoch_common_header_size(number_of_segments);
+
+	hdr->flags		|= MA_USB_HDR_FLAGS_TIMESTAMP;
+	hdr->type		 = (u8)MA_USB_HDR_TYPE_DATA_REQ(ISOCHTRANSFER);
+	hdr->data.headers	 = (u16)number_of_segments;
+	hdr->data.i_flags	 = MA_USB_DATA_IFLAGS_HDR_FMT_STD |
+				      MA_USB_DATA_IFLAGS_ASAP;
+	hdr_opt_isochtransfer	    = mausb_hdr_isochtransfer_optional_hdr(hdr);
+	hdr_isochtransfer	    = mausb_get_isochtransfer_hdr(hdr);
+	hdr_isochtransfer->req_id   = event->data.req_id;
+	hdr_isochtransfer->seq_n    = seq_n;
+	hdr_isochtransfer->segments = number_of_packets;
+
+	hdr_isochtransfer->presentation_time = MA_USB_TRANSFER_RESERVED;
+
+	hdr_opt_isochtransfer->timestamp = MA_USB_TRANSFER_RESERVED;
+	hdr_opt_isochtransfer->mtd	 = MA_USB_TRANSFER_RESERVED;
+
+	hdr->length = (u16)length + payload_size;
+
+	return hdr;
+}
+
+static int
+mausb_init_isoch_out_header_chunk(struct ma_usb_hdr_common *common_hdr,
+				  struct list_head *chunks_list,
+				  u32 *num_of_data_chunks,
+				  u32 num_of_packets)
+{
+	u32 header_size =
+		__mausb_calculate_isoch_common_header_size(num_of_packets);
+	int status = mausb_add_data_chunk(common_hdr, header_size, chunks_list);
+
+	if (!status)
+		++(*num_of_data_chunks);
+
+	return status;
+}
+
+static
+int mausb_prepare_isoch_out_transfer_packet(struct ma_usb_hdr_common *hdr,
+					    struct mausb_event *event,
+					    struct mausb_urb_ctx *urb_ctx,
+					    struct mausb_kvec_data_wrapper *
+					    result_data_wrapper)
+{
+	u32 num_of_data_chunks	       = 0;
+	u32 num_of_payload_data_chunks = 0;
+	u32 segment_number	       = event->data.isoch_seg_num;
+	u32 payload_data_size;
+	struct list_head chunks_list;
+	struct list_head payload_data_chunks;
+	int status = 0;
+
+	INIT_LIST_HEAD(&chunks_list);
+
+	/* Initialize data chunk for MAUSB header and add it to chunks list */
+	if (mausb_init_isoch_out_header_chunk(hdr, &chunks_list,
+					      &num_of_data_chunks,
+					      segment_number) < 0) {
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	/* Get data chunks for data payload to send */
+	INIT_LIST_HEAD(&payload_data_chunks);
+	payload_data_size = hdr->length -
+		__mausb_calculate_isoch_common_header_size(segment_number);
+
+	if (mausb_data_iterator_read(&urb_ctx->iterator, payload_data_size,
+				     &payload_data_chunks,
+				     &num_of_payload_data_chunks) < 0) {
+		mausb_pr_err("Data iterator read failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+	list_splice_tail(&payload_data_chunks, &chunks_list);
+	num_of_data_chunks += num_of_payload_data_chunks;
+
+	/* Map all data chunks to data wrapper */
+	if (mausb_init_data_wrapper(result_data_wrapper, &chunks_list,
+				    num_of_data_chunks) < 0) {
+		mausb_pr_err("Data wrapper init failed");
+		status = -ENOMEM;
+		goto cleanup_data_chunks;
+	}
+
+cleanup_data_chunks:
+	mausb_cleanup_chunks_list(&chunks_list);
+	return status;
+}
+
+static int mausb_create_and_send_isoch_transfer_req(struct mausb_device *dev,
+						    struct mausb_event *event,
+						    struct mausb_urb_ctx
+						    *urb_ctx, u32 *seq_n,
+						    u32 payload_size,
+						    u32 start_of_segments,
+						    u32 number_of_segments)
+{
+	struct ma_usb_hdr_common *hdr;
+	struct mausb_kvec_data_wrapper data_to_send;
+	int status;
+	enum mausb_channel channel;
+
+	hdr = __mausb_create_isoch_out_transfer_packet(event, urb_ctx,
+						       (u16)payload_size,
+						       *seq_n,
+						       start_of_segments,
+						       number_of_segments);
+	if (!hdr) {
+		mausb_pr_alert("Isoch transfer packet alloc failed");
+		return -ENOMEM;
+	}
+	*seq_n = (*seq_n + 1) % (MA_USB_TRANSFER_SEQN_MAX + 1);
+
+	status = mausb_prepare_isoch_out_transfer_packet(hdr, event, urb_ctx,
+							 &data_to_send);
+	if (status < 0) {
+		mausb_pr_alert("Failed to prepare transfer packet");
+		kfree(hdr);
+		return status;
+	}
+
+	channel = mausb_transfer_type_to_channel(event->data.transfer_type);
+	status = mausb_send_data(dev, channel, &data_to_send);
+
+	kfree(hdr);
+	kfree(data_to_send.kvec);
+
+	return status;
+}
+
+static inline int __mausb_send_isoch_out_packet(struct mausb_device *dev,
+						struct mausb_event *event,
+						struct mausb_urb_ctx *urb_ctx,
+						u32 *seq_n,
+						u32 *starting_segments,
+						u32 *rem_transfer_buf,
+						u32 *payload_size, u32 index)
+{
+	int status = mausb_create_and_send_isoch_transfer_req(dev, event,
+					urb_ctx, seq_n, *payload_size,
+					*starting_segments,
+					index - *starting_segments);
+	if (status < 0) {
+		mausb_pr_err("ISOCH transfer request create and send failed");
+		return status;
+	}
+	*starting_segments = index;
+	*rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	*payload_size	   = 0;
+
+	return 0;
+}
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx)
+{
+	u32   starting_segments = 0;
+	u32   rem_transfer_buf  = MAX_ISOCH_ASAP_PACKET_SIZE;
+	struct urb *urb = (struct urb *)mausb_event->data.urb;
+	u32 number_of_packets = (u32)urb->number_of_packets;
+	u32 payload_size   = 0;
+	u32 chunk_size;
+	u32 seq_n	   = 0;
+	int status;
+	u32 i;
+
+	for (i = 0; i < number_of_packets; ++i) {
+		if (i < number_of_packets - 1)
+			chunk_size = urb->iso_frame_desc[i + 1].offset -
+					urb->iso_frame_desc[i].offset;
+		else
+			chunk_size =
+				mausb_data_iterator_length(&urb_ctx->iterator) -
+						urb->iso_frame_desc[i].offset;
+
+		if (chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE >
+		    rem_transfer_buf) {
+			if (payload_size == 0) {
+				mausb_pr_warn("Fragmentation");
+			} else {
+				status = __mausb_send_isoch_out_packet
+						(ma_dev, mausb_event, urb_ctx,
+						 &seq_n, &starting_segments,
+						 &rem_transfer_buf,
+						 &payload_size, i);
+				if (status < 0)
+					return status;
+				i--;
+				continue;
+			}
+		} else {
+			rem_transfer_buf -=
+				chunk_size + MAUSB_ISOCH_STANDARD_FORMAT_SIZE;
+			payload_size += chunk_size;
+		}
+
+		if (i == number_of_packets - 1 || rem_transfer_buf == 0) {
+			status = __mausb_send_isoch_out_packet
+					(ma_dev, mausb_event, urb_ctx, &seq_n,
+					 &starting_segments, &rem_transfer_buf,
+					 &payload_size, i + 1);
+			if (status < 0)
+				return status;
+		}
+	}
+	return 0;
+}
+
+int mausb_receive_isoch_out(struct mausb_event *event)
+{
+	struct urb *urb = (struct urb *)event->data.urb;
+	int status = 0;
+	u16 i;
+
+	mausb_pr_debug("transfer_size=%d, rem_transfer_size=%d, status=%d",
+		       event->data.transfer_size, event->data.rem_transfer_size,
+		       event->status);
+
+	for (i = 0; i < urb->number_of_packets; ++i)
+		urb->iso_frame_desc[i].status = event->status;
+
+	mausb_complete_request(urb, event->data.payload_size, event->status);
+
+	return status;
+}
diff --git a/drivers/usb/mausb_host/hpal_data.h b/drivers/usb/mausb_host/hpal_data.h
new file mode 100644
index 000000000000..8d9650e5fb75
--- /dev/null
+++ b/drivers/usb/mausb_host/hpal_data.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 - 2020 DisplayLink (UK) Ltd.
+ */
+#ifndef __MAUSB_HPAL_DATA_H__
+#define __MAUSB_HPAL_DATA_H__
+
+#include <linux/types.h>
+
+#include "hpal_events.h"
+
+int mausb_send_in_data_msg(struct mausb_device *dev, struct mausb_event *event);
+void mausb_receive_in_data(struct mausb_event *event,
+			   struct mausb_urb_ctx *urb_ctx);
+
+int mausb_send_out_data_msg(struct mausb_device *dev, struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+void mausb_receive_out_data(struct mausb_event *event,
+			    struct mausb_urb_ctx *urb_ctx);
+
+#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);
+
+int mausb_send_isoch_out_msg(struct mausb_device *ma_dev,
+			     struct mausb_event *mausb_event,
+			     struct mausb_urb_ctx *urb_ctx);
+int mausb_receive_isoch_out(struct mausb_event *event);
+
+#endif /* __MAUSB_HPAL_DATA_H__ */
-- 
2.17.1





[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux