msm: ipc: Add support to handle version 2 header

IPC Router Message header is 32 bytes in size since each element uses
4 bytes in size regardless of the data range being represented.

Add a version 2 header format that compacts the header into 16 bytes.

Change-Id: I5e6cfc0672ca1dc3365b31c26d2ce4b3c6230cfc
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 7263832..3249398 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -298,6 +298,8 @@
 		pr_err("%s: failure\n", __func__);
 		return NULL;
 	}
+	memcpy(&(cloned_pkt->hdr), &(pkt->hdr), sizeof(struct rr_header_v1));
+	/* TODO: Copy optional headers, if available */
 
 	pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
 	if (!pkt_fragment_q) {
@@ -323,6 +325,7 @@
 		kfree_skb(temp_skb);
 	}
 	kfree(pkt_fragment_q);
+	/* TODO: Free optional headers, if present */
 	kfree(cloned_pkt);
 	return NULL;
 }
@@ -361,6 +364,7 @@
 		kfree_skb(temp_skb);
 	}
 	kfree(pkt->pkt_fragment_q);
+	/* TODO: Free Optional headers, if present */
 	kfree(pkt);
 	return;
 }
@@ -373,6 +377,8 @@
 	int first = 1, offset = 0;
 	int skb_size, data_size;
 	void *data;
+	int last = 1;
+	int align_size;
 
 	skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
 	if (!skb_head) {
@@ -382,10 +388,13 @@
 	skb_queue_head_init(skb_head);
 
 	data_size = buf_len;
+	align_size = ALIGN_SIZE(data_size);
 	while (offset != buf_len) {
 		skb_size = data_size;
 		if (first)
 			skb_size += IPC_ROUTER_HDR_SIZE;
+		if (last)
+			skb_size += align_size;
 
 		skb = alloc_skb(skb_size, GFP_KERNEL);
 		if (!skb) {
@@ -394,6 +403,7 @@
 				goto buf_to_skb_error;
 			}
 			data_size = data_size / 2;
+			last = 0;
 			continue;
 		}
 
@@ -407,6 +417,7 @@
 		skb_queue_tail(skb_head, skb);
 		offset += data_size;
 		data_size = buf_len - offset;
+		last = 1;
 	}
 	return skb_head;
 
@@ -461,6 +472,328 @@
 	kfree(skb_head);
 }
 
+/**
+ * extract_header_v1() - Extract IPC Router header of version 1
+ * @pkt: Packet structure into which the header has to be extraced.
+ * @skb: SKB from which the header has to be extracted.
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ */
+static int extract_header_v1(struct rr_packet *pkt, struct sk_buff *skb)
+{
+	if (!pkt || !skb) {
+		pr_err("%s: Invalid pkt or skb\n", __func__);
+		return -EINVAL;
+	}
+
+	memcpy(&pkt->hdr, skb->data, sizeof(struct rr_header_v1));
+	skb_pull(skb, sizeof(struct rr_header_v1));
+	pkt->length -= sizeof(struct rr_header_v1);
+	return 0;
+}
+
+/**
+ * extract_header_v2() - Extract IPC Router header of version 2
+ * @pkt: Packet structure into which the header has to be extraced.
+ * @skb: SKB from which the header has to be extracted.
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ */
+static int extract_header_v2(struct rr_packet *pkt, struct sk_buff *skb)
+{
+	struct rr_header_v2 *hdr;
+
+	if (!pkt || !skb) {
+		pr_err("%s: Invalid pkt or skb\n", __func__);
+		return -EINVAL;
+	}
+
+	hdr = (struct rr_header_v2 *)skb->data;
+	pkt->hdr.version = (uint32_t)hdr->version;
+	pkt->hdr.type = (uint32_t)hdr->type;
+	pkt->hdr.src_node_id = (uint32_t)hdr->src_node_id;
+	pkt->hdr.src_port_id = (uint32_t)hdr->src_port_id;
+	pkt->hdr.size = (uint32_t)hdr->size;
+	pkt->hdr.control_flag = (uint32_t)hdr->control_flag;
+	pkt->hdr.dst_node_id = (uint32_t)hdr->dst_node_id;
+	pkt->hdr.dst_port_id = (uint32_t)hdr->dst_port_id;
+	skb_pull(skb, sizeof(struct rr_header_v2));
+	pkt->length -= sizeof(struct rr_header_v2);
+	return 0;
+}
+
+/**
+ * extract_header() - Extract IPC Router header
+ * @pkt: Packet from which the header has to be extraced.
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ *
+ * This function will check if the header version is v1 or v2 and invoke
+ * the corresponding helper function to extract the IPC Router header.
+ */
+static int extract_header(struct rr_packet *pkt)
+{
+	struct sk_buff *temp_skb;
+	int ret;
+
+	if (!pkt) {
+		pr_err("%s: NULL PKT\n", __func__);
+		return -EINVAL;
+	}
+
+	temp_skb = skb_peek(pkt->pkt_fragment_q);
+	if (!temp_skb || !temp_skb->data) {
+		pr_err("%s: No SKBs in skb_queue\n", __func__);
+		return -EINVAL;
+	}
+
+	if (temp_skb->data[0] == IPC_ROUTER_V1) {
+		ret = extract_header_v1(pkt, temp_skb);
+	} else if (temp_skb->data[0] == IPC_ROUTER_V2) {
+		ret = extract_header_v2(pkt, temp_skb);
+		/* TODO: Extract optional headers if present */
+	} else {
+		pr_err("%s: Invalid Header version %02x\n",
+			__func__, temp_skb->data[0]);
+		print_hex_dump(KERN_ERR, "Header: ", DUMP_PREFIX_ADDRESS,
+			       16, 1, temp_skb->data, pkt->length, true);
+		return -EINVAL;
+	}
+	return ret;
+}
+
+/**
+ * calc_tx_header_size() - Calculate header size to be reserved in SKB
+ * @pkt: Packet in which the space for header has to be reserved.
+ * @dst_xprt_info: XPRT through which the destination is reachable.
+ *
+ * @return: required header size on success,
+ *          starndard Linux error codes on failure.
+ *
+ * This function is used to calculate the header size that has to be reserved
+ * in a transmit SKB. The header size is calculated based on the XPRT through
+ * which the destination node is reachable.
+ */
+static int calc_tx_header_size(struct rr_packet *pkt,
+			       struct msm_ipc_router_xprt_info *dst_xprt_info)
+{
+	int hdr_size = 0;
+	int xprt_version = 0;
+	struct msm_ipc_routing_table_entry *rt_entry;
+	struct msm_ipc_router_xprt_info *xprt_info = dst_xprt_info;
+
+	if (!pkt) {
+		pr_err("%s: NULL PKT\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!xprt_info) {
+		rt_entry = lookup_routing_table(pkt->hdr.dst_node_id);
+		if (!rt_entry || !(rt_entry->xprt_info)) {
+			pr_err("%s: Node %d is not up\n",
+				__func__, pkt->hdr.dst_node_id);
+			return -ENODEV;
+		}
+
+		xprt_info = rt_entry->xprt_info;
+	}
+	if (xprt_info)
+		xprt_version = xprt_info->xprt->get_version(xprt_info->xprt);
+
+	if (xprt_version == IPC_ROUTER_V1) {
+		pkt->hdr.version = IPC_ROUTER_V1;
+		hdr_size = sizeof(struct rr_header_v1);
+	} else if (xprt_version == IPC_ROUTER_V2) {
+		pkt->hdr.version = IPC_ROUTER_V2;
+		hdr_size = sizeof(struct rr_header_v2);
+		/* TODO: Calculate optional header length, if present */
+	} else {
+		pr_err("%s: Invalid xprt_version %d\n",
+			__func__, xprt_version);
+		hdr_size = -EINVAL;
+	}
+
+	return hdr_size;
+}
+
+/**
+ * prepend_header_v1() - Prepend IPC Router header of version 1
+ * @pkt: Packet structure which contains the header info to be prepended.
+ * @hdr_size: Size of the header
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ */
+static int prepend_header_v1(struct rr_packet *pkt, int hdr_size)
+{
+	struct sk_buff *temp_skb;
+	struct rr_header_v1 *hdr;
+
+	if (!pkt || hdr_size <= 0) {
+		pr_err("%s: Invalid input parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	temp_skb = skb_peek(pkt->pkt_fragment_q);
+	if (!temp_skb || !temp_skb->data) {
+		pr_err("%s: No SKBs in skb_queue\n", __func__);
+		return -EINVAL;
+	}
+
+	if (skb_headroom(temp_skb) < hdr_size) {
+		temp_skb = alloc_skb(hdr_size, GFP_KERNEL);
+		if (!temp_skb) {
+			pr_err("%s: Could not allocate SKB of size %d\n",
+				__func__, hdr_size);
+			return -ENOMEM;
+		}
+	}
+
+	hdr = (struct rr_header_v1 *)skb_push(temp_skb, hdr_size);
+	memcpy(hdr, &pkt->hdr, hdr_size);
+	if (temp_skb != skb_peek(pkt->pkt_fragment_q))
+		skb_queue_head(pkt->pkt_fragment_q, temp_skb);
+	pkt->length += hdr_size;
+	return 0;
+}
+
+/**
+ * prepend_header_v2() - Prepend IPC Router header of version 2
+ * @pkt: Packet structure which contains the header info to be prepended.
+ * @hdr_size: Size of the header
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ */
+static int prepend_header_v2(struct rr_packet *pkt, int hdr_size)
+{
+	struct sk_buff *temp_skb;
+	struct rr_header_v2 *hdr;
+
+	if (!pkt || hdr_size <= 0) {
+		pr_err("%s: Invalid input parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	temp_skb = skb_peek(pkt->pkt_fragment_q);
+	if (!temp_skb || !temp_skb->data) {
+		pr_err("%s: No SKBs in skb_queue\n", __func__);
+		return -EINVAL;
+	}
+
+	if (skb_headroom(temp_skb) < hdr_size) {
+		temp_skb = alloc_skb(hdr_size, GFP_KERNEL);
+		if (!temp_skb) {
+			pr_err("%s: Could not allocate SKB of size %d\n",
+				__func__, hdr_size);
+			return -ENOMEM;
+		}
+	}
+
+	hdr = (struct rr_header_v2 *)skb_push(temp_skb, hdr_size);
+	hdr->version = (uint8_t)pkt->hdr.version;
+	hdr->type = (uint8_t)pkt->hdr.type;
+	hdr->control_flag = (uint16_t)pkt->hdr.control_flag;
+	hdr->size = (uint32_t)pkt->hdr.size;
+	hdr->src_node_id = (uint16_t)pkt->hdr.src_node_id;
+	hdr->src_port_id = (uint16_t)pkt->hdr.src_port_id;
+	hdr->dst_node_id = (uint16_t)pkt->hdr.dst_node_id;
+	hdr->dst_port_id = (uint16_t)pkt->hdr.dst_port_id;
+	/* TODO: Add optional headers, if present */
+	if (temp_skb != skb_peek(pkt->pkt_fragment_q))
+		skb_queue_head(pkt->pkt_fragment_q, temp_skb);
+	pkt->length += hdr_size;
+	return 0;
+}
+
+/**
+ * prepend_header() - Prepend IPC Router header
+ * @pkt: Packet structure which contains the header info to be prepended.
+ * @xprt_info: XPRT through which the packet is transmitted.
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ *
+ * This function prepends the header to the packet to be transmitted. The
+ * IPC Router header version to be prepended depends on the XPRT through
+ * which the destination is reachable.
+ */
+static int prepend_header(struct rr_packet *pkt,
+			  struct msm_ipc_router_xprt_info *xprt_info)
+{
+	int hdr_size;
+	struct sk_buff *temp_skb;
+
+	if (!pkt) {
+		pr_err("%s: NULL PKT\n", __func__);
+		return -EINVAL;
+	}
+
+	temp_skb = skb_peek(pkt->pkt_fragment_q);
+	if (!temp_skb || !temp_skb->data) {
+		pr_err("%s: No SKBs in skb_queue\n", __func__);
+		return -EINVAL;
+	}
+
+	hdr_size = calc_tx_header_size(pkt, xprt_info);
+	if (hdr_size <= 0)
+		return hdr_size;
+
+	if (pkt->hdr.version == IPC_ROUTER_V1)
+		return prepend_header_v1(pkt, hdr_size);
+	else if (pkt->hdr.version == IPC_ROUTER_V2)
+		return prepend_header_v2(pkt, hdr_size);
+	else
+		return -EINVAL;
+}
+
+/**
+ * defragment_pkt() - Defragment and linearize the packet
+ * @pkt: Packet to be linearized.
+ *
+ * @return: 0 on success, standard Linux error codes on failure.
+ *
+ * Some packets contain fragments of data over multiple SKBs. If an XPRT
+ * does not supported fragmented writes, linearize multiple SKBs into one
+ * single SKB.
+ */
+static int defragment_pkt(struct rr_packet *pkt)
+{
+	struct sk_buff *dst_skb, *src_skb, *temp_skb;
+	int offset = 0, buf_len = 0, copy_len;
+	void *buf;
+	int align_size;
+
+	if (!pkt || pkt->length <= 0) {
+		pr_err("%s: Invalid PKT\n", __func__);
+		return -EINVAL;
+	}
+
+	if (skb_queue_len(pkt->pkt_fragment_q) == 1)
+		return 0;
+
+	align_size = ALIGN_SIZE(pkt->length);
+	dst_skb = alloc_skb(pkt->length + align_size, GFP_KERNEL);
+	if (!dst_skb) {
+		pr_err("%s: could not allocate one skb of size %d\n",
+			__func__, pkt->length);
+		return -ENOMEM;
+	}
+	buf = skb_put(dst_skb, pkt->length);
+	buf_len = pkt->length;
+
+	skb_queue_walk(pkt->pkt_fragment_q, src_skb) {
+		copy_len =  buf_len < src_skb->len ? buf_len : src_skb->len;
+		memcpy(buf + offset, src_skb->data, copy_len);
+		offset += copy_len;
+		buf_len -= copy_len;
+	}
+
+	while (!skb_queue_empty(pkt->pkt_fragment_q)) {
+		temp_skb = skb_dequeue(pkt->pkt_fragment_q);
+		kfree_skb(temp_skb);
+	}
+	skb_queue_tail(pkt->pkt_fragment_q, dst_skb);
+	return 0;
+}
+
 static int post_pkt_to_port(struct msm_ipc_port *port_ptr,
 			    struct rr_packet *pkt, int clone)
 {
@@ -515,7 +848,7 @@
 	down_read(&local_ports_lock_lha2);
 	do {
 		next_port_id++;
-		if ((next_port_id & 0xFFFFFFFE) == 0xFFFFFFFE)
+		if ((next_port_id & IPC_ROUTER_ADDRESS) == IPC_ROUTER_ADDRESS)
 			next_port_id = 1;
 
 		key = (next_port_id & (LP_HASH_SIZE - 1));
@@ -932,7 +1265,7 @@
 {
 	struct rr_packet *pkt;
 	struct sk_buff *ipc_rtr_pkt;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	int pkt_size;
 	void *data;
 	struct sk_buff_head *pkt_fragment_q;
@@ -973,32 +1306,33 @@
 	skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
 	data = skb_put(ipc_rtr_pkt, sizeof(*msg));
 	memcpy(data, msg, sizeof(*msg));
-	hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
-	if (!hdr) {
-		pr_err("%s: skb_push failed\n", __func__);
-		kfree_skb(ipc_rtr_pkt);
-		kfree(pkt_fragment_q);
-		kfree(pkt);
-		return -ENOMEM;
-	}
+	skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
+	pkt->pkt_fragment_q = pkt_fragment_q;
+	pkt->length = sizeof(*msg);
 
-	hdr->version = IPC_ROUTER_VERSION;
+	hdr = &(pkt->hdr);
+	hdr->version = IPC_ROUTER_V1;
 	hdr->type = msg->cmd;
 	hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
 	hdr->src_port_id = IPC_ROUTER_ADDRESS;
-	hdr->confirm_rx = 0;
+	hdr->control_flag = 0;
 	hdr->size = sizeof(*msg);
 	if (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)
 		hdr->dst_node_id = dst_node_id;
 	else
 		hdr->dst_node_id = xprt_info->remote_node_id;
 	hdr->dst_port_id = IPC_ROUTER_ADDRESS;
-	skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
-	pkt->pkt_fragment_q = pkt_fragment_q;
-	pkt->length = pkt_size;
 
 	mutex_lock(&xprt_info->tx_lock_lhb2);
-	ret = xprt_info->xprt->write(pkt, pkt_size, xprt_info->xprt);
+	ret = prepend_header(pkt, xprt_info);
+	if (ret < 0) {
+		mutex_unlock(&xprt_info->tx_lock_lhb2);
+		pr_err("%s: Prepend Header failed\n", __func__);
+		release_pkt(pkt);
+		return ret;
+	}
+
+	ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
 	mutex_unlock(&xprt_info->tx_lock_lhb2);
 
 	release_pkt(pkt);
@@ -1074,7 +1408,7 @@
 {
 	struct rr_packet *pkt;
 	struct sk_buff *ipc_rtr_pkt;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	int pkt_size;
 	void *data;
 	struct sk_buff_head *pkt_fragment_q;
@@ -1094,7 +1428,7 @@
 	}
 	skb_queue_head_init(pkt_fragment_q);
 
-	pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
+	pkt_size = sizeof(*msg);
 	ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
 	if (!ipc_rtr_pkt) {
 		pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
@@ -1103,22 +1437,14 @@
 		return -ENOMEM;
 	}
 
-	skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
 	data = skb_put(ipc_rtr_pkt, sizeof(*msg));
 	memcpy(data, msg, sizeof(*msg));
-	hdr = (struct rr_header *)skb_push(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
-	if (!hdr) {
-		pr_err("%s: skb_push failed\n", __func__);
-		kfree_skb(ipc_rtr_pkt);
-		kfree(pkt_fragment_q);
-		kfree(pkt);
-		return -ENOMEM;
-	}
-	hdr->version = IPC_ROUTER_VERSION;
+	hdr = &(pkt->hdr);
+	hdr->version = IPC_ROUTER_V1;
 	hdr->type = msg->cmd;
 	hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
 	hdr->src_port_id = IPC_ROUTER_ADDRESS;
-	hdr->confirm_rx = 0;
+	hdr->control_flag = 0;
 	hdr->size = sizeof(*msg);
 	hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
 	hdr->dst_port_id = IPC_ROUTER_ADDRESS;
@@ -1164,70 +1490,60 @@
 	return 0;
 }
 
-static int relay_msg(struct msm_ipc_router_xprt_info *xprt_info,
-		     struct rr_packet *pkt)
-{
-	struct msm_ipc_router_xprt_info *fwd_xprt_info;
-
-	if (!xprt_info || !pkt)
-		return -EINVAL;
-
-	down_read(&xprt_info_list_lock_lha5);
-	list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
-		mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
-		if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
-			fwd_xprt_info->xprt->write(pkt, pkt->length,
-						   fwd_xprt_info->xprt);
-		mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
-	}
-	up_read(&xprt_info_list_lock_lha5);
-	return 0;
-}
-
 static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
 		       struct rr_packet *pkt)
 {
-	uint32_t dst_node_id;
-	struct sk_buff *head_pkt;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	struct msm_ipc_router_xprt_info *fwd_xprt_info;
 	struct msm_ipc_routing_table_entry *rt_entry;
 	int ret = 0;
+	int fwd_xprt_option;
 
 	if (!xprt_info || !pkt)
 		return -EINVAL;
 
-	head_pkt = skb_peek(pkt->pkt_fragment_q);
-	if (!head_pkt)
-		return -EINVAL;
-
-	hdr = (struct rr_header *)head_pkt->data;
-	dst_node_id = hdr->dst_node_id;
+	hdr = &(pkt->hdr);
 	down_read(&routing_table_lock_lha3);
-	rt_entry = lookup_routing_table(dst_node_id);
+	rt_entry = lookup_routing_table(hdr->dst_node_id);
 	if (!(rt_entry) || !(rt_entry->xprt_info)) {
-		up_read(&routing_table_lock_lha3);
 		pr_err("%s: Routing table not initialized\n", __func__);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto fm_error1;
 	}
 
 	down_read(&rt_entry->lock_lha4);
 	fwd_xprt_info = rt_entry->xprt_info;
+	ret = prepend_header(pkt, fwd_xprt_info);
+	if (ret < 0) {
+		pr_err("%s: Prepend Header failed\n", __func__);
+		goto fm_error2;
+	}
+	fwd_xprt_option = fwd_xprt_info->xprt->get_option(fwd_xprt_info->xprt);
+	if (!(fwd_xprt_option & FRAG_PKT_WRITE_ENABLE)) {
+		ret = defragment_pkt(pkt);
+		if (ret < 0)
+			goto fm_error2;
+	}
+
+	mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
 	if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) {
 		pr_err("%s: Discarding Command to route back\n", __func__);
 		ret = -EINVAL;
-		goto fwd_msg_out;
+		goto fm_error3;
 	}
 
 	if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) {
 		pr_err("%s: DST in the same cluster\n", __func__);
-		goto fwd_msg_out;
+		ret = 0;
+		goto fm_error3;
 	}
-	mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
 	fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt);
+
+fm_error3:
 	mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
-fwd_msg_out:
+fm_error2:
 	up_read(&rt_entry->lock_lha4);
+fm_error1:
 	up_read(&routing_table_lock_lha3);
 
 	return ret;
@@ -1470,7 +1786,7 @@
 }
 
 static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
-			     struct rr_header *hdr)
+			     struct rr_header_v1 *hdr)
 {
 	int i, rc = 0;
 	union rr_control_msg ctl;
@@ -1508,7 +1824,7 @@
 
 	/* Send a reply HELLO message */
 	memset(&ctl, 0, sizeof(ctl));
-	ctl.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
+	ctl.hello.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
 	rc = msm_ipc_router_send_control_msg(xprt_info, &ctl,
 						IPC_ROUTER_DUMMY_DEST_NODE);
 	if (rc < 0) {
@@ -1654,7 +1970,7 @@
 	 * to the cluster from which this message is received. Notify the
 	 * local clients waiting for this service.
 	 */
-	relay_msg(xprt_info, pkt);
+	relay_ctl_msg(xprt_info, msg);
 	post_control_ports(pkt);
 	return 0;
 }
@@ -1677,7 +1993,7 @@
 		 * belong to the cluster from which this message is received.
 		 * Notify the local clients communicating with the service.
 		 */
-		relay_msg(xprt_info, pkt);
+		relay_ctl_msg(xprt_info, msg);
 		post_control_ports(pkt);
 	}
 	up_write(&server_list_lock_lha2);
@@ -1697,7 +2013,7 @@
 		msm_ipc_router_destroy_remote_port(rport_ptr);
 	up_write(&routing_table_lock_lha3);
 
-	relay_msg(xprt_info, pkt);
+	relay_ctl_msg(xprt_info, msg);
 	post_control_ports(pkt);
 	return 0;
 }
@@ -1707,26 +2023,20 @@
 {
 	union rr_control_msg *msg;
 	int rc = 0;
-	struct sk_buff *temp_ptr;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 
-	if (pkt->length != (IPC_ROUTER_HDR_SIZE + sizeof(*msg))) {
+	if (pkt->length != sizeof(*msg)) {
 		pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length,
-			(IPC_ROUTER_HDR_SIZE + sizeof(*msg)));
+			sizeof(*msg));
 		return -EINVAL;
 	}
 
-	temp_ptr = skb_peek(pkt->pkt_fragment_q);
-	if (!temp_ptr) {
-		pr_err("%s: pkt_fragment_q is empty\n", __func__);
-		return -EINVAL;
+	hdr = &(pkt->hdr);
+	msg = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, sizeof(*msg));
+	if (!msg) {
+		pr_err("%s: Error extracting control msg\n", __func__);
+		return -ENOMEM;
 	}
-	hdr = (struct rr_header *)temp_ptr->data;
-	if (!hdr) {
-		pr_err("%s: No data inside the skb\n", __func__);
-		return -EINVAL;
-	}
-	msg = (union rr_control_msg *)((char *)hdr + IPC_ROUTER_HDR_SIZE);
 
 	switch (msg->cmd) {
 	case IPC_ROUTER_CTRL_CMD_HELLO:
@@ -1752,17 +2062,17 @@
 		RR("o UNKNOWN(%08x)\n", msg->cmd);
 		rc = -ENOSYS;
 	}
-
+	kfree(msg);
 	return rc;
 }
 
 static void do_read_data(struct work_struct *work)
 {
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	struct rr_packet *pkt = NULL;
 	struct msm_ipc_port *port_ptr;
-	struct sk_buff *head_skb;
 	struct msm_ipc_router_remote_port *rport_ptr;
+	int ret;
 
 	struct msm_ipc_router_xprt_info *xprt_info =
 		container_of(work,
@@ -1777,24 +2087,15 @@
 			goto fail_data;
 		}
 
-		head_skb = skb_peek(pkt->pkt_fragment_q);
-		if (!head_skb) {
-			pr_err("%s: head_skb is invalid\n", __func__);
+		ret = extract_header(pkt);
+		if (ret < 0)
 			goto fail_data;
-		}
-
-		hdr = (struct rr_header *)(head_skb->data);
+		hdr = &(pkt->hdr);
 		RAW("ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
 		     hdr->version, hdr->type, hdr->src_node_id,
-		     hdr->src_port_id, hdr->confirm_rx, hdr->size,
+		     hdr->src_port_id, hdr->control_flag, hdr->size,
 		     hdr->dst_node_id, hdr->dst_port_id);
 
-		if (hdr->version != IPC_ROUTER_VERSION) {
-			pr_err("version %d != %d\n",
-				hdr->version, IPC_ROUTER_VERSION);
-			goto fail_data;
-		}
-
 		if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
 		    ((hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX) ||
 		     (hdr->type == IPC_ROUTER_CTRL_CMD_DATA))) {
@@ -1803,8 +2104,7 @@
 			continue;
 		}
 
-		if ((hdr->dst_port_id == IPC_ROUTER_ADDRESS) ||
-		    (hdr->type == IPC_ROUTER_CTRL_CMD_HELLO)) {
+		if (hdr->type != IPC_ROUTER_CTRL_CMD_DATA) {
 			process_control_msg(xprt_info, pkt);
 			release_pkt(pkt);
 			continue;
@@ -1819,7 +2119,7 @@
 				(hdr->src_port_id & 0xffffff),
 				(hdr->dst_node_id << 24) |
 				(hdr->dst_port_id & 0xffffff),
-				(hdr->type << 24) | (hdr->confirm_rx << 16) |
+				(hdr->type << 24) | (hdr->control_flag << 16) |
 				(hdr->size & 0xffff));
 		}
 #endif
@@ -1969,8 +2269,7 @@
 			uint32_t port_id,
 			struct sk_buff_head *data)
 {
-	struct sk_buff *head_skb;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	struct msm_ipc_port *port_ptr;
 	struct rr_packet *pkt;
 	int ret_len;
@@ -1985,28 +2284,15 @@
 		pr_err("%s: New pkt create failed\n", __func__);
 		return -ENOMEM;
 	}
-
-	head_skb = skb_peek(pkt->pkt_fragment_q);
-	if (!head_skb) {
-		pr_err("%s: pkt_fragment_q is empty\n", __func__);
-		release_pkt(pkt);
-		return -EINVAL;
-	}
-	hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
-	if (!hdr) {
-		pr_err("%s: Prepend Header failed\n", __func__);
-		release_pkt(pkt);
-		return -ENOMEM;
-	}
-	hdr->version = IPC_ROUTER_VERSION;
+	hdr = &(pkt->hdr);
+	hdr->version = IPC_ROUTER_V1;
 	hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
 	hdr->src_node_id = src->this_port.node_id;
 	hdr->src_port_id = src->this_port.port_id;
 	hdr->size = pkt->length;
-	hdr->confirm_rx = 0;
+	hdr->control_flag = 0;
 	hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
 	hdr->dst_port_id = port_id;
-	pkt->length += IPC_ROUTER_HDR_SIZE;
 
 	down_read(&local_ports_lock_lha2);
 	port_ptr = msm_ipc_router_lookup_local_port(port_id);
@@ -2029,35 +2315,26 @@
 				struct msm_ipc_router_remote_port *rport_ptr,
 				struct rr_packet *pkt)
 {
-	struct sk_buff *head_skb;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	struct msm_ipc_router_xprt_info *xprt_info;
 	struct msm_ipc_routing_table_entry *rt_entry;
 	struct msm_ipc_resume_tx_port  *resume_tx_port;
+	struct sk_buff *temp_skb;
+	int xprt_option;
 	int ret;
+	int align_size;
 
 	if (!rport_ptr || !src || !pkt)
 		return -EINVAL;
 
-	head_skb = skb_peek(pkt->pkt_fragment_q);
-	if (!head_skb) {
-		pr_err("%s: pkt_fragment_q is empty\n", __func__);
-		return -EINVAL;
-	}
-	hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
-	if (!hdr) {
-		pr_err("%s: Prepend Header failed\n", __func__);
-		return -ENOMEM;
-	}
-	hdr->version = IPC_ROUTER_VERSION;
+	hdr = &(pkt->hdr);
 	hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
 	hdr->src_node_id = src->this_port.node_id;
 	hdr->src_port_id = src->this_port.port_id;
 	hdr->size = pkt->length;
-	hdr->confirm_rx = 0;
+	hdr->control_flag = 0;
 	hdr->dst_node_id = rport_ptr->node_id;
 	hdr->dst_port_id = rport_ptr->port_id;
-	pkt->length += IPC_ROUTER_HDR_SIZE;
 
 	mutex_lock(&rport_ptr->quota_lock_lhb2);
 	if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
@@ -2085,7 +2362,7 @@
 	}
 	rport_ptr->tx_quota_cnt++;
 	if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
-		hdr->confirm_rx = 1;
+		hdr->control_flag |= CONTROL_FLAG_CONFIRM_RX;
 	mutex_unlock(&rport_ptr->quota_lock_lhb2);
 
 	rt_entry = lookup_routing_table(hdr->dst_node_id);
@@ -2096,6 +2373,25 @@
 	}
 	down_read(&rt_entry->lock_lha4);
 	xprt_info = rt_entry->xprt_info;
+	ret = prepend_header(pkt, xprt_info);
+	if (ret < 0) {
+		up_read(&rt_entry->lock_lha4);
+		pr_err("%s: Prepend Header failed\n", __func__);
+		return ret;
+	}
+	xprt_option = xprt_info->xprt->get_option(xprt_info->xprt);
+	if (!(xprt_option & FRAG_PKT_WRITE_ENABLE)) {
+		ret = defragment_pkt(pkt);
+		if (ret < 0) {
+			up_read(&rt_entry->lock_lha4);
+			return ret;
+		}
+	}
+
+	temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
+	align_size = ALIGN_SIZE(pkt->length);
+	skb_put(temp_skb, align_size);
+	pkt->length += align_size;
 	mutex_lock(&xprt_info->tx_lock_lhb2);
 	ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
 	mutex_unlock(&xprt_info->tx_lock_lhb2);
@@ -2109,10 +2405,10 @@
 
 	RAW_HDR("[w rr_h] "
 		"ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
-		"confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
+		"control_flag=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
 		hdr->version, type_to_str(hdr->type),
 		hdr->src_node_id, hdr->src_port_id,
-		hdr->confirm_rx, hdr->size,
+		hdr->control_flag, hdr->size,
 		hdr->dst_node_id, hdr->dst_port_id);
 
 #if defined(CONFIG_MSM_SMD_LOGGING)
@@ -2125,13 +2421,13 @@
 			(hdr->src_port_id & 0xffffff),
 			(hdr->dst_node_id << 24) |
 			(hdr->dst_port_id & 0xffffff),
-			(hdr->type << 24) | (hdr->confirm_rx << 16) |
+			(hdr->type << 24) | (hdr->control_flag << 16) |
 			(hdr->size & 0xffff));
 	}
 #endif
 #endif
 
-	return pkt->length;
+	return hdr->size;
 }
 
 int msm_ipc_router_send_to(struct msm_ipc_port *src,
@@ -2250,7 +2546,7 @@
 static int msm_ipc_router_send_resume_tx(void *data)
 {
 	union rr_control_msg msg;
-	struct rr_header *hdr = (struct rr_header *)data;
+	struct rr_header_v1 *hdr = (struct rr_header_v1 *)data;
 	struct msm_ipc_routing_table_entry *rt_entry;
 	int ret;
 
@@ -2280,14 +2576,12 @@
 }
 
 int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
-			struct sk_buff_head **data,
+			struct rr_packet **read_pkt,
 			size_t buf_len)
 {
 	struct rr_packet *pkt;
-	struct sk_buff *head_skb;
-	int ret;
 
-	if (!port_ptr || !data)
+	if (!port_ptr || !read_pkt)
 		return -EINVAL;
 
 	mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
@@ -2297,26 +2591,19 @@
 	}
 
 	pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
-	if ((buf_len) && ((pkt->length - IPC_ROUTER_HDR_SIZE) > buf_len)) {
+	if ((buf_len) && (pkt->hdr.size > buf_len)) {
 		mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
 		return -ETOOSMALL;
 	}
 	list_del(&pkt->list);
 	if (list_empty(&port_ptr->port_rx_q))
 		wake_unlock(&port_ptr->port_rx_wake_lock);
-	*data = pkt->pkt_fragment_q;
-	ret = pkt->length;
+	*read_pkt = pkt;
 	mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
-	kfree(pkt);
-	head_skb = skb_peek(*data);
-	if (!head_skb) {
-		pr_err("%s: Socket Buffer not found", __func__);
-		return -EFAULT;
-	}
-	if (((struct rr_header *)(head_skb->data))->confirm_rx)
-		msm_ipc_router_send_resume_tx((void *)(head_skb->data));
+	if (pkt->hdr.control_flag & CONTROL_FLAG_CONFIRM_RX)
+		msm_ipc_router_send_resume_tx(&pkt->hdr);
 
-	return ret;
+	return pkt->length;
 }
 
 /**
@@ -2364,7 +2651,7 @@
 /**
  * msm_ipc_router_recv_from() - Recieve messages destined to a local port.
  * @port_ptr: Pointer to the local port
- * @data : Pointer to the socket buffer head
+ * @pkt : Pointer to the router-to-router packet
  * @src: Pointer to local port address
  * @timeout: < 0 timeout indicates infinite wait till a message arrives.
  *	     > 0 timeout indicates the wait time.
@@ -2385,31 +2672,30 @@
  * of bytes that are read.
  */
 int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
-			     struct sk_buff_head **data,
+			     struct rr_packet **pkt,
 			     struct msm_ipc_addr *src,
 			     long timeout)
 {
 	int ret, data_len, align_size;
 	struct sk_buff *temp_skb;
-	struct rr_header *hdr = NULL;
+	struct rr_header_v1 *hdr = NULL;
 
-	if (!port_ptr || !data) {
+	if (!port_ptr || !pkt) {
 		pr_err("%s: Invalid pointers being passed\n", __func__);
 		return -EINVAL;
 	}
 
-	*data = NULL;
+	*pkt = NULL;
 
 	ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
 	if (ret)
 		return ret;
 
-	ret = msm_ipc_router_read(port_ptr, data, 0);
-	if (ret <= 0 || !(*data))
+	ret = msm_ipc_router_read(port_ptr, pkt, 0);
+	if (ret <= 0 || !(*pkt))
 		return ret;
 
-	temp_skb = skb_peek(*data);
-	hdr = (struct rr_header *)(temp_skb->data);
+	hdr = &((*pkt)->hdr);
 	if (src) {
 		src->addrtype = MSM_IPC_ADDR_ID;
 		src->addr.port_addr.node_id = hdr->src_node_id;
@@ -2417,10 +2703,9 @@
 	}
 
 	data_len = hdr->size;
-	skb_pull(temp_skb, IPC_ROUTER_HDR_SIZE);
 	align_size = ALIGN_SIZE(data_len);
 	if (align_size) {
-		temp_skb = skb_peek_tail(*data);
+		temp_skb = skb_peek_tail((*pkt)->pkt_fragment_q);
 		skb_trim(temp_skb, (temp_skb->len - align_size));
 	}
 	return data_len;
@@ -2431,11 +2716,10 @@
 			    unsigned char **data,
 			    unsigned int *len)
 {
-	struct sk_buff_head *in_skb_head;
+	struct rr_packet *pkt;
 	int ret;
 
-	ret = msm_ipc_router_recv_from(port_ptr, &in_skb_head, src, 0);
-
+	ret = msm_ipc_router_recv_from(port_ptr, &pkt, src, 0);
 	if (ret < 0) {
 		if (ret != -ENOMSG)
 			pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
@@ -2443,12 +2727,12 @@
 		return ret;
 	}
 
-	*data = msm_ipc_router_skb_to_buf(in_skb_head, ret);
+	*data = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, ret);
 	if (!(*data))
 		pr_err("%s: Buf conversion failed\n", __func__);
 
 	*len = ret;
-	msm_ipc_router_free_skb(in_skb_head);
+	release_pkt(pkt);
 	return 0;
 }
 
diff --git a/arch/arm/mach-msm/ipc_router.h b/arch/arm/mach-msm/ipc_router.h
index 7bfc52b..a788295 100644
--- a/arch/arm/mach-msm/ipc_router.h
+++ b/arch/arm/mach-msm/ipc_router.h
@@ -24,12 +24,17 @@
 #include <net/sock.h>
 
 /* definitions for the R2R wire protcol */
-#define IPC_ROUTER_VERSION			1
+#define IPC_ROUTER_V1		1
+/*
+ * Ambiguous definition but will enable multiplexing IPC_ROUTER_V2 packets
+ * with an existing alternate transport in user-space, if needed.
+ */
+#define IPC_ROUTER_V2		3
 
-#define IPC_ROUTER_CLIENT_BCAST_ID		0xffffffff
-#define IPC_ROUTER_ADDRESS			0xfffffffe
+#define IPC_ROUTER_ADDRESS			0x0000FFFF
 
 #define IPC_ROUTER_NID_LOCAL			1
+#define MAX_IPC_PKT_SIZE 66000
 
 #define IPC_ROUTER_CTRL_CMD_DATA		1
 #define IPC_ROUTER_CTRL_CMD_HELLO		2
@@ -55,6 +60,11 @@
 #define ALL_SERVICE 0xFFFFFFFF
 #define ALL_INSTANCE 0xFFFFFFFF
 
+#define CONTROL_FLAG_CONFIRM_RX 0x1
+#define CONTROL_FLAG_OPT_HDR 0x2
+
+#define FRAG_PKT_WRITE_ENABLE 0x1
+
 enum {
 	CLIENT_PORT,
 	SERVER_PORT,
@@ -68,10 +78,22 @@
 	MULTI_LINK_MODE,
 };
 
+/**
+ * rr_control_msg - Control message structure
+ * @cmd: Command identifier for HELLO message in Version 1.
+ * @hello: Message structure for HELLO message in Version 2.
+ * @srv: Message structure for NEW_SERVER/REMOVE_SERVER events.
+ * @cli: Message structure for REMOVE_CLIENT event.
+ */
 union rr_control_msg {
 	uint32_t cmd;
 	struct {
 		uint32_t cmd;
+		uint32_t magic;
+		uint32_t capability;
+	} hello;
+	struct {
+		uint32_t cmd;
 		uint32_t service;
 		uint32_t instance;
 		uint32_t node_id;
@@ -84,22 +106,67 @@
 	} cli;
 };
 
-struct rr_header {
+/**
+ * rr_header_v1 - IPC Router header version 1
+ * @version: Version information.
+ * @type: IPC Router Message Type.
+ * @src_node_id: Source Node ID of the message.
+ * @src_port_id: Source Port ID of the message.
+ * @control_flag: Flag to indicate flow control.
+ * @size: Size of the IPC Router payload.
+ * @dst_node_id: Destination Node ID of the message.
+ * @dst_port_id: Destination Port ID of the message.
+ */
+struct rr_header_v1 {
 	uint32_t version;
 	uint32_t type;
 	uint32_t src_node_id;
 	uint32_t src_port_id;
-	uint32_t confirm_rx;
+	uint32_t control_flag;
 	uint32_t size;
 	uint32_t dst_node_id;
 	uint32_t dst_port_id;
 };
 
-#define IPC_ROUTER_HDR_SIZE sizeof(struct rr_header)
-#define MAX_IPC_PKT_SIZE 66000
+/**
+ * rr_header_v2 - IPC Router header version 2
+ * @version: Version information.
+ * @type: IPC Router Message Type.
+ * @control_flag: Flags to indicate flow control, optional header etc.
+ * @size: Size of the IPC Router payload.
+ * @src_node_id: Source Node ID of the message.
+ * @src_port_id: Source Port ID of the message.
+ * @dst_node_id: Destination Node ID of the message.
+ * @dst_port_id: Destination Port ID of the message.
+ */
+struct rr_header_v2 {
+	uint8_t version;
+	uint8_t type;
+	uint16_t control_flag;
+	uint32_t size;
+	uint16_t src_node_id;
+	uint16_t src_port_id;
+	uint16_t dst_node_id;
+	uint16_t dst_port_id;
+} __attribute__((__packed__));
 
+union rr_header {
+	struct rr_header_v1 hdr_v1;
+	struct rr_header_v2 hdr_v2;
+};
+
+#define IPC_ROUTER_HDR_SIZE sizeof(union rr_header)
+
+/**
+ * rr_packet - Router to Router packet structure
+ * @list: Pointer to prev & next packets in a port's rx list.
+ * @hdr: Header information extracted from or prepended to a packet.
+ * @pkt_fragment_q: Queue of SKBs containing payload.
+ * @length: Length of data in the chain of SKBs
+ */
 struct rr_packet {
 	struct list_head list;
+	struct rr_header_v1 hdr;
 	struct sk_buff_head *pkt_fragment_q;
 	uint32_t length;
 };
@@ -110,11 +177,28 @@
 	void *default_pil;
 };
 
+/**
+ * msm_ipc_router_xprt - Structure to hold XPRT specific information
+ * @name: Name of the XPRT.
+ * @link_id: Network cluster ID to which the XPRT belongs to.
+ * @priv: XPRT's private data.
+ * @get_version: Method to get header version supported by the XPRT.
+ * @get_option: Method to get XPRT specific options.
+ * @read_avail: Method to get data size available to be read from the XPRT.
+ * @read: Method to read data from the XPRT.
+ * @write_avail: Method to get write space available in the XPRT.
+ * @write: Method to write data to the XPRT.
+ * @close: Method to close the XPRT.
+ * @sft_close_done: Method to indicate to the XPRT that handling of reset
+ *                  event is complete.
+ */
 struct msm_ipc_router_xprt {
 	char *name;
 	uint32_t link_id;
 	void *priv;
 
+	int (*get_version)(struct msm_ipc_router_xprt *xprt);
+	int (*get_option)(struct msm_ipc_router_xprt *xprt);
 	int (*read_avail)(struct msm_ipc_router_xprt *xprt);
 	int (*read)(void *data, uint32_t len,
 		    struct msm_ipc_router_xprt *xprt);
@@ -141,12 +225,12 @@
 			   struct sk_buff_head *data,
 			   struct msm_ipc_addr *dest);
 int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
-			struct sk_buff_head **data,
+			struct rr_packet **pkt,
 			size_t buf_len);
 int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr);
 
 int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
-		      struct sk_buff_head **data,
+		      struct rr_packet **pkt,
 		      struct msm_ipc_addr *src_addr,
 		      long timeout);
 int msm_ipc_router_register_server(struct msm_ipc_port *server_port,
diff --git a/arch/arm/mach-msm/ipc_router_smd_xprt.c b/arch/arm/mach-msm/ipc_router_smd_xprt.c
index 38a838d..9c7ae90 100644
--- a/arch/arm/mach-msm/ipc_router_smd_xprt.c
+++ b/arch/arm/mach-msm/ipc_router_smd_xprt.c
@@ -55,6 +55,8 @@
 	int ss_reset;
 	void *pil;
 	struct completion sft_close_complete;
+	unsigned xprt_version;
+	unsigned xprt_option;
 };
 
 struct msm_ipc_router_smd_xprt_work {
@@ -71,13 +73,14 @@
 	char xprt_name[XPRT_NAME_LEN];
 	uint32_t edge;
 	uint32_t link_id;
+	unsigned xprt_version;
 };
 
 struct msm_ipc_router_smd_xprt_config smd_xprt_cfg[] = {
-	{"RPCRPY_CNTL", "ipc_rtr_smd_rpcrpy_cntl", SMD_APPS_MODEM, 1},
-	{"IPCRTR", "ipc_rtr_smd_ipcrtr", SMD_APPS_MODEM, 1},
-	{"IPCRTR", "ipc_rtr_q6_ipcrtr", SMD_APPS_QDSP, 1},
-	{"IPCRTR", "ipc_rtr_wcnss_ipcrtr", SMD_APPS_WCNSS, 1},
+	{"RPCRPY_CNTL", "ipc_rtr_smd_rpcrpy_cntl", SMD_APPS_MODEM, 1, 1},
+	{"IPCRTR", "ipc_rtr_smd_ipcrtr", SMD_APPS_MODEM, 1, 1},
+	{"IPCRTR", "ipc_rtr_q6_ipcrtr", SMD_APPS_QDSP, 1, 1},
+	{"IPCRTR", "ipc_rtr_wcnss_ipcrtr", SMD_APPS_WCNSS, 1, 1},
 };
 
 static struct msm_ipc_router_smd_xprt smd_remote_xprt[NUM_SMD_XPRTS];
@@ -95,6 +98,28 @@
 	return -ENODEV;
 }
 
+static int msm_ipc_router_smd_get_xprt_version(
+	struct msm_ipc_router_xprt *xprt)
+{
+	struct msm_ipc_router_smd_xprt *smd_xprtp;
+	if (!xprt)
+		return -EINVAL;
+	smd_xprtp = container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
+
+	return (int)smd_xprtp->xprt_version;
+}
+
+static int msm_ipc_router_smd_get_xprt_option(
+	struct msm_ipc_router_xprt *xprt)
+{
+	struct msm_ipc_router_smd_xprt *smd_xprtp;
+	if (!xprt)
+		return -EINVAL;
+	smd_xprtp = container_of(xprt, struct msm_ipc_router_smd_xprt, xprt);
+
+	return (int)smd_xprtp->xprt_option;
+}
+
 static int msm_ipc_router_smd_remote_write_avail(
 	struct msm_ipc_router_xprt *xprt)
 {
@@ -110,7 +135,6 @@
 {
 	struct rr_packet *pkt = (struct rr_packet *)data;
 	struct sk_buff *ipc_rtr_pkt;
-	int align_sz, align_data = 0;
 	int offset, sz_written = 0;
 	int ret, num_retries = 0;
 	unsigned long flags;
@@ -123,9 +147,7 @@
 	if (!len || pkt->length != len)
 		return -EINVAL;
 
-	align_sz = ALIGN_SIZE(pkt->length);
-	while ((ret = smd_write_start(smd_xprtp->channel,
-				      (len + align_sz))) < 0) {
+	while ((ret = smd_write_start(smd_xprtp->channel, len)) < 0) {
 		spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
 		if (smd_xprtp->ss_reset) {
 			spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock,
@@ -143,7 +165,7 @@
 		num_retries++;
 	}
 
-	D("%s: Ready to write\n", __func__);
+	D("%s: Ready to write %d bytes\n", __func__, len);
 	skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
 		offset = 0;
 		while (offset < ipc_rtr_pkt->len) {
@@ -175,30 +197,6 @@
 		  __func__, offset, xprt->name);
 	}
 
-	if (align_sz) {
-		if (smd_write_avail(smd_xprtp->channel) < align_sz)
-			smd_enable_read_intr(smd_xprtp->channel);
-
-		wait_event(smd_xprtp->write_avail_wait_q,
-			((smd_write_avail(smd_xprtp->channel) >=
-			 align_sz) || smd_xprtp->ss_reset));
-		smd_disable_read_intr(smd_xprtp->channel);
-		spin_lock_irqsave(&smd_xprtp->ss_reset_lock, flags);
-		if (smd_xprtp->ss_reset) {
-			spin_unlock_irqrestore(
-				&smd_xprtp->ss_reset_lock, flags);
-			pr_err("%s: %s chnl reset\n",
-				__func__, xprt->name);
-			return -ENETRESET;
-		}
-		spin_unlock_irqrestore(&smd_xprtp->ss_reset_lock,
-					flags);
-
-		smd_write_segment(smd_xprtp->channel,
-				  &align_data, align_sz, 0);
-		D("%s: Wrote %d align bytes over %s\n",
-		  __func__, align_sz, xprt->name);
-	}
 	if (!smd_write_end(smd_xprtp->channel))
 		D("%s: Finished writing\n", __func__);
 	return len;
@@ -453,6 +451,10 @@
 
 	smd_remote_xprt[id].xprt.name = smd_xprt_cfg[id].xprt_name;
 	smd_remote_xprt[id].xprt.link_id = smd_xprt_cfg[id].link_id;
+	smd_remote_xprt[id].xprt.get_version =
+		msm_ipc_router_smd_get_xprt_version;
+	smd_remote_xprt[id].xprt.get_option =
+		msm_ipc_router_smd_get_xprt_option;
 	smd_remote_xprt[id].xprt.read_avail = NULL;
 	smd_remote_xprt[id].xprt.read = NULL;
 	smd_remote_xprt[id].xprt.write_avail =
@@ -468,6 +470,8 @@
 	INIT_DELAYED_WORK(&smd_remote_xprt[id].read_work, smd_xprt_read_data);
 	spin_lock_init(&smd_remote_xprt[id].ss_reset_lock);
 	smd_remote_xprt[id].ss_reset = 0;
+	smd_remote_xprt[id].xprt_version = smd_xprt_cfg[id].xprt_version;
+	smd_remote_xprt[id].xprt_option = FRAG_PKT_WRITE_ENABLE;
 
 	smd_remote_xprt[id].pil = msm_ipc_load_subsystem(
 					smd_xprt_cfg[id].edge);
diff --git a/arch/arm/mach-msm/ipc_socket.c b/arch/arm/mach-msm/ipc_socket.c
index ea27c71..d96d459 100644
--- a/arch/arm/mach-msm/ipc_socket.c
+++ b/arch/arm/mach-msm/ipc_socket.c
@@ -131,12 +131,15 @@
 	int i, copied, first = 1;
 	int data_size = 0, request_size, offset;
 	void *data;
+	int last = 0;
+	int align_size;
 
 	for (i = 0; i < num_sect; i++)
 		data_size += msg_sect[i].iov_len;
 
 	if (!data_size)
 		return NULL;
+	align_size = ALIGN_SIZE(data_size);
 
 	msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
 	if (!msg_head) {
@@ -148,10 +151,14 @@
 	for (copied = 1, i = 0; copied && (i < num_sect); i++) {
 		data_size = msg_sect[i].iov_len;
 		offset = 0;
+		if (i == (num_sect - 1))
+			last = 1;
 		while (offset != msg_sect[i].iov_len) {
 			request_size = data_size;
 			if (first)
 				request_size += IPC_ROUTER_HDR_SIZE;
+			if (last)
+				request_size += align_size;
 
 			msg = alloc_skb(request_size, GFP_KERNEL);
 			if (!msg) {
@@ -161,6 +168,7 @@
 					goto msg_build_failure;
 				}
 				data_size = data_size / 2;
+				last = 0;
 				continue;
 			}
 
@@ -182,6 +190,8 @@
 			skb_queue_tail(msg_head, msg);
 			offset += data_size;
 			data_size = msg_sect[i].iov_len - offset;
+			if (i == (num_sect - 1))
+				last = 1;
 		}
 	}
 	return msg_head;
@@ -196,24 +206,23 @@
 }
 
 static int msm_ipc_router_extract_msg(struct msghdr *m,
-				      struct sk_buff_head *msg_head)
+				      struct rr_packet *pkt)
 {
 	struct sockaddr_msm_ipc *addr;
-	struct rr_header *hdr;
+	struct rr_header_v1 *hdr;
 	struct sk_buff *temp;
 	union rr_control_msg *ctl_msg;
 	int offset = 0, data_len = 0, copy_len;
 
-	if (!m || !msg_head) {
+	if (!m || !pkt) {
 		pr_err("%s: Invalid pointers passed\n", __func__);
 		return -EINVAL;
 	}
 	addr = (struct sockaddr_msm_ipc *)m->msg_name;
 
-	temp = skb_peek(msg_head);
-	hdr = (struct rr_header *)(temp->data);
+	hdr = &(pkt->hdr);
 	if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
-		skb_pull(temp, IPC_ROUTER_HDR_SIZE);
+		temp = skb_peek(pkt->pkt_fragment_q);
 		ctl_msg = (union rr_control_msg *)(temp->data);
 		addr->family = AF_MSM_IPC;
 		addr->address.addrtype = MSM_IPC_ADDR_ID;
@@ -222,7 +231,7 @@
 		m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
 		return offset;
 	}
-	if (addr && (hdr->src_port_id != IPC_ROUTER_ADDRESS)) {
+	if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_DATA)) {
 		addr->family = AF_MSM_IPC;
 		addr->address.addrtype = MSM_IPC_ADDR_ID;
 		addr->address.addr.port_addr.node_id = hdr->src_node_id;
@@ -231,8 +240,7 @@
 	}
 
 	data_len = hdr->size;
-	skb_pull(temp, IPC_ROUTER_HDR_SIZE);
-	skb_queue_walk(msg_head, temp) {
+	skb_queue_walk(pkt->pkt_fragment_q, temp) {
 		copy_len = data_len < temp->len ? data_len : temp->len;
 		if (copy_to_user(m->msg_iov->iov_base + offset, temp->data,
 				 copy_len)) {
@@ -245,22 +253,6 @@
 	return offset;
 }
 
-static void msm_ipc_router_release_msg(struct sk_buff_head *msg_head)
-{
-	struct sk_buff *temp;
-
-	if (!msg_head) {
-		pr_err("%s: Invalid msg pointer\n", __func__);
-		return;
-	}
-
-	while (!skb_queue_empty(msg_head)) {
-		temp = skb_dequeue(msg_head);
-		kfree_skb(temp);
-	}
-	kfree(msg_head);
-}
-
 static int msm_ipc_router_create(struct net *net,
 				 struct socket *sock,
 				 int protocol,
@@ -385,8 +377,10 @@
 	if (ipc_buf)
 		msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
 	ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
-	if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
-		ret = total_len;
+	if (ret != total_len) {
+		pr_err("%s: Send_to failure %d\n", __func__, ret);
+		ret = -EFAULT;
+	}
 
 out_sendmsg:
 	release_sock(sk);
@@ -398,7 +392,7 @@
 {
 	struct sock *sk = sock->sk;
 	struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
-	struct sk_buff_head *msg;
+	struct rr_packet *pkt;
 	struct sk_buff *ipc_buf;
 	long timeout;
 	int ret;
@@ -420,18 +414,17 @@
 		return ret;
 	}
 
-	ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
-	if (ret <= 0 || !msg) {
+	ret = msm_ipc_router_read(port_ptr, &pkt, buf_len);
+	if (ret <= 0 || !pkt) {
 		release_sock(sk);
 		return ret;
 	}
 
-	ret = msm_ipc_router_extract_msg(m, msg);
-	ipc_buf = skb_peek(msg);
+	ret = msm_ipc_router_extract_msg(m, pkt);
+	ipc_buf = skb_peek(pkt->pkt_fragment_q);
 	if (ipc_buf)
 		msm_ipc_router_ipc_log(IPC_RECV, ipc_buf, port_ptr);
-	msm_ipc_router_release_msg(msg);
-	msg = NULL;
+	release_pkt(pkt);
 	release_sock(sk);
 	return ret;
 }
@@ -460,7 +453,7 @@
 
 	switch (cmd) {
 	case IPC_ROUTER_IOCTL_GET_VERSION:
-		n = IPC_ROUTER_VERSION;
+		n = IPC_ROUTER_V1;
 		ret = put_user(n, (unsigned int *)arg);
 		break;