Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 20d6af5d authored by Karthikeyan Ramasubramanian's avatar Karthikeyan Ramasubramanian
Browse files

drivers: ipc_hsic: Add support for fragmentation and re-assembly



In order to exchange big packets between application processor and
off-chip peripherals, add support for hsic transport layer fragmentation
in write path and reassembly in read path.

Change-Id: I25a7fcfe06067df59871adfc22539689210f9e80
Signed-off-by: default avatarKarthikeyan Ramasubramanian <kramasub@codeaurora.org>
parent a4ff7275
Loading
Loading
Loading
Loading
+71 −26
Original line number Diff line number Diff line
@@ -213,6 +213,9 @@ static int msm_ipc_router_hsic_remote_write(void *data,
	struct ipc_bridge_platform_data *pdata;
	struct msm_ipc_router_hsic_xprt *hsic_xprtp;
	int ret;
	uint32_t bytes_written = 0;
	uint32_t bytes_to_write;
	unsigned char *tx_data;

	if (!pkt || pkt->length != len || !xprt) {
		IPC_RTR_ERR("%s: Invalid input parameters\n", __func__);
@@ -247,9 +250,29 @@ static int msm_ipc_router_hsic_remote_write(void *data,
		return -EINVAL;
	}
	D("%s: About to write %d bytes\n", __func__, len);
	ret = pdata->write(hsic_xprtp->pdev, skb->data, skb->len);
	if (ret == skb->len)
		ret = len;

	while (bytes_written < len) {
		bytes_to_write = min_t(uint32_t, (skb->len - bytes_written),
				       pdata->max_write_size);
		tx_data = skb->data + bytes_written;
		ret = pdata->write(hsic_xprtp->pdev, tx_data, bytes_to_write);
		if (ret < 0) {
			IPC_RTR_ERR("%s: Error writing data %d\n",
				    __func__, ret);
			break;
		}
		if (ret != bytes_to_write)
			IPC_RTR_ERR("%s: Partial write %d < %d, retrying...\n",
				    __func__, ret, bytes_to_write);
		bytes_written += bytes_to_write;
	}
	if (bytes_written == len) {
		ret = bytes_written;
	} else if (ret > 0 && bytes_written != len) {
		IPC_RTR_ERR("%s: Fault writing data %d != %d\n",
			    __func__, bytes_written, len);
		ret = -EFAULT;
	}
	D("%s: Finished writing %d bytes\n", __func__, len);
	mutex_unlock(&hsic_xprtp->ss_reset_lock);
	return ret;
@@ -294,7 +317,9 @@ static int msm_ipc_router_hsic_remote_close(
 */
static void hsic_xprt_read_data(struct work_struct *work)
{
	int pkt_size;
	int bytes_to_read;
	int bytes_read;
	int skb_size;
	struct sk_buff *skb = NULL;
	struct ipc_bridge_platform_data *pdata;
	struct delayed_work *rwork = to_delayed_work(work);
@@ -319,33 +344,53 @@ static void hsic_xprt_read_data(struct work_struct *work)
		}
		D("%s: Allocated rr_packet\n", __func__);

		while (!skb) {
			skb = alloc_skb(pdata->max_read_size, GFP_KERNEL);
		bytes_to_read = 0;
		skb_size = pdata->max_read_size;
		do {
			do {
				skb = alloc_skb(skb_size, GFP_KERNEL);
				if (skb)
					break;
			IPC_RTR_ERR("%s: Couldn't alloc SKB\n", __func__);
				IPC_RTR_ERR("%s: Couldn't alloc SKB\n",
					    __func__);
				msleep(100);
		}
		pkt_size = pdata->read(hsic_xprtp->pdev, skb->data,
			} while (!skb);
			bytes_read = pdata->read(hsic_xprtp->pdev, skb->data,
						 pdata->max_read_size);
		if (pkt_size < 0) {
			if (bytes_read < 0) {
				IPC_RTR_ERR("%s: Error %d @ read operation\n",
				__func__, pkt_size);
					    __func__, bytes_read);
				kfree_skb(skb);
			kfree(hsic_xprtp->in_pkt->pkt_fragment_q);
			kfree(hsic_xprtp->in_pkt);
			break;
				goto out_read_data;
			}
			if (!bytes_to_read) {
				bytes_to_read = ipc_router_peek_pkt_size(
						skb->data);
				if (bytes_to_read < 0) {
					IPC_RTR_ERR("%s: Invalid size %d\n",
						__func__, bytes_to_read);
					kfree_skb(skb);
					goto out_read_data;
				}
			}
		skb_put(skb, pkt_size);
			bytes_to_read -= bytes_read;
			skb_put(skb, bytes_read);
			skb_queue_tail(hsic_xprtp->in_pkt->pkt_fragment_q, skb);
		hsic_xprtp->in_pkt->length = pkt_size;
		D("%s: Packet size read %d\n", __func__, pkt_size);
			hsic_xprtp->in_pkt->length += bytes_read;
			skb_size = min_t(uint32_t, pdata->max_read_size,
					 (uint32_t)bytes_to_read);
		} while (bytes_to_read > 0);

		D("%s: Packet size read %d\n",
		  __func__, hsic_xprtp->in_pkt->length);
		msm_ipc_router_xprt_notify(&hsic_xprtp->xprt,
			IPC_ROUTER_XPRT_EVENT_DATA, (void *)hsic_xprtp->in_pkt);
		release_pkt(hsic_xprtp->in_pkt);
		hsic_xprtp->in_pkt = NULL;
		skb = NULL;
	}
out_read_data:
	release_pkt(hsic_xprtp->in_pkt);
	hsic_xprtp->in_pkt = NULL;
}

/**
+12 −0
Original line number Diff line number Diff line
@@ -138,6 +138,18 @@ struct rr_packet *create_pkt(struct sk_buff_head *data);
struct rr_packet *clone_pkt(struct rr_packet *pkt);
void release_pkt(struct rr_packet *pkt);

/**
 * ipc_router_peek_pkt_size() - Peek into the packet header to get potential packet size
 * @data: Starting address of the packet which points to router header.
 *
 * @returns: potential packet size on success, < 0 on error.
 *
 * This function is used by the underlying transport abstraction layer to
 * peek into the potential packet size of an incoming packet. This information
 * is used to perform link layer fragmentation and re-assembly
 */
int ipc_router_peek_pkt_size(char *data);

#if defined CONFIG_MSM_IPC_ROUTER_SMD_XPRT
extern void *msm_ipc_load_default_node(void);

+33 −0
Original line number Diff line number Diff line
@@ -867,6 +867,39 @@ static int post_pkt_to_port(struct msm_ipc_port *port_ptr,
	return 0;
}

/**
 * ipc_router_peek_pkt_size() - Peek into the packet header to get potential packet size
 * @data: Starting address of the packet which points to router header.
 *
 * @returns: potential packet size on success, < 0 on error.
 *
 * This function is used by the underlying transport abstraction layer to
 * peek into the potential packet size of an incoming packet. This information
 * is used to perform link layer fragmentation and re-assembly
 */
int ipc_router_peek_pkt_size(char *data)
{
	int size;

	if (!data) {
		pr_err("%s: NULL PKT\n", __func__);
		return -EINVAL;
	}

	/* FUTURE: Calculate optional header len in V2 header*/
	if (data[0] == IPC_ROUTER_V1)
		size = ((struct rr_header_v1 *)data)->size +
			sizeof(struct rr_header_v1);
	else if (data[0] == IPC_ROUTER_V2)
		size = ((struct rr_header_v2 *)data)->size +
			sizeof(struct rr_header_v2);
	else
		return -EINVAL;

	size += ALIGN_SIZE(size);
	return size;
}

static int post_control_ports(struct rr_packet *pkt)
{
	struct msm_ipc_port *port_ptr;