Add a packet oriented SMD Transport to be used by the MSM_IPC Router.

Change-Id: Id992a23acad5b7ad262138163730dfd5b7a56ab1
Signed-off-by: Karthikeyan Ramasubramanian <[email protected]>
---
 net/msm_ipc/Kconfig                   |    9 ++
 net/msm_ipc/Makefile                  |    1 +
 net/msm_ipc/msm_ipc_router.c          |   86 +++++++++++++
 net/msm_ipc/msm_ipc_router_smd_xprt.c |  226 +++++++++++++++++++++++++++++++++
 4 files changed, 322 insertions(+), 0 deletions(-)
 create mode 100644 net/msm_ipc/msm_ipc_router_smd_xprt.c

diff --git a/net/msm_ipc/Kconfig b/net/msm_ipc/Kconfig
index 9114d7d..5368a40 100644
--- a/net/msm_ipc/Kconfig
+++ b/net/msm_ipc/Kconfig
@@ -13,3 +13,12 @@ menuconfig MSM_IPC
 
        Say Y (or M) here if you build for a phone product (e.g. Android)
        that uses MSM_IPC as transport, if unsure say N.
+
+config MSM_IPC_ROUTER_SMD_XPRT
+       tristate "MSM_IPC_ROUTER SMD Transport"
+       depends on MSM_IPC
+       depends on MSM_SMD
+       default n
+       ---help---
+       This will provide a packet interface to the MSM_IPC Router over SMD.
+       Say Y if you will be using a MSM_IPC over SMD transport.
diff --git a/net/msm_ipc/Makefile b/net/msm_ipc/Makefile
index b9a0dfb..4f73e67 100644
--- a/net/msm_ipc/Makefile
+++ b/net/msm_ipc/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_MSM_IPC) := msm_ipc.o
+obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += msm_ipc_router_smd_xprt.o
 
 msm_ipc-y += msm_ipc_router.o msm_ipc_socket.o
diff --git a/net/msm_ipc/msm_ipc_router.c b/net/msm_ipc/msm_ipc_router.c
index 0cd0a02..80bd931 100644
--- a/net/msm_ipc/msm_ipc_router.c
+++ b/net/msm_ipc/msm_ipc_router.c
@@ -272,6 +272,7 @@ void release_pkt(struct rr_packet *pkt)
        kfree(pkt);
        return;
 }
+EXPORT_SYMBOL(release_pkt);
 
 static int post_control_ports(struct rr_packet *pkt)
 {
@@ -1636,6 +1637,91 @@ int msm_ipc_router_close(void)
        return 0;
 }
 
+#if defined(CONFIG_MSM_IPC_ROUTER_SMD_XPRT)
+static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
+{
+       struct msm_ipc_router_xprt_info *xprt_info;
+       struct msm_ipc_routing_table_entry *rt_entry;
+
+       xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info),
+                           GFP_KERNEL);
+       if (!xprt_info)
+               return -ENOMEM;
+
+       xprt_info->xprt = xprt;
+       xprt_info->initialized = 0;
+       xprt_info->remote_node_id = -1;
+       INIT_LIST_HEAD(&xprt_info->pkt_list);
+       init_waitqueue_head(&xprt_info->read_wait);
+       mutex_init(&xprt_info->rx_lock);
+       mutex_init(&xprt_info->tx_lock);
+       xprt_info->need_len = 0;
+       INIT_WORK(&xprt_info->read_data, do_read_data);
+       INIT_LIST_HEAD(&xprt_info->list);
+
+       xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
+       if (!xprt_info->workqueue) {
+               kfree(xprt_info);
+               return -ENOMEM;
+       }
+
+       if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) {
+               xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL;
+               xprt_info->initialized = 1;
+       }
+
+       mutex_lock(&xprt_info_list_lock);
+       list_add_tail(&xprt_info->list, &xprt_info_list);
+       mutex_unlock(&xprt_info_list_lock);
+
+       mutex_lock(&routing_table_lock);
+       if (!routing_table_inited) {
+               init_routing_table();
+               rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
+               add_routing_table_entry(rt_entry);
+               routing_table_inited = 1;
+       }
+       mutex_unlock(&routing_table_lock);
+
+       queue_work(xprt_info->workqueue, &xprt_info->read_data);
+
+       xprt->priv = xprt_info;
+
+       return 0;
+}
+
+void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
+                               unsigned event,
+                               void *data)
+{
+       struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
+       struct rr_packet *pkt;
+
+       if (event == IPC_ROUTER_XPRT_EVENT_OPEN) {
+               msm_ipc_router_add_xprt(xprt);
+               return;
+       }
+
+       if (!data)
+               return;
+
+       while (!xprt_info) {
+               msleep(100);
+               xprt_info = xprt->priv;
+       }
+
+       pkt = clone_pkt((struct rr_packet *)data);
+       if (!pkt)
+               return;
+
+       mutex_lock(&xprt_info->rx_lock);
+       list_add_tail(&pkt->list, &xprt_info->pkt_list);
+       wake_up(&xprt_info->read_wait);
+       mutex_unlock(&xprt_info->rx_lock);
+}
+EXPORT_SYMBOL(msm_ipc_router_xprt_notify);
+#endif
+
 static int __init msm_ipc_router_init(void)
 {
        int i, ret;
diff --git a/net/msm_ipc/msm_ipc_router_smd_xprt.c 
b/net/msm_ipc/msm_ipc_router_smd_xprt.c
new file mode 100644
index 0000000..1402a8b
--- /dev/null
+++ b/net/msm_ipc/msm_ipc_router_smd_xprt.c
@@ -0,0 +1,226 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * IPC ROUTER SMD XPRT module.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+#include <mach/msm_smd.h>
+
+#include "msm_ipc_router.h"
+
+#define MIN_FRAG_SZ (IPC_ROUTER_HDR_SIZE + sizeof(union rr_control_msg))
+
+struct msm_ipc_router_smd_xprt {
+       struct msm_ipc_router_xprt xprt;
+
+       smd_channel_t *channel;
+};
+
+static struct msm_ipc_router_smd_xprt smd_remote_xprt;
+
+static void smd_xprt_read_data(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work_read_data, smd_xprt_read_data);
+static struct workqueue_struct *smd_xprt_workqueue;
+
+static wait_queue_head_t write_avail_wait_q;
+static struct rr_packet *in_pkt;
+static int is_partial_in_pkt;
+
+static int msm_ipc_router_smd_remote_write_avail(void)
+{
+       return smd_write_avail(smd_remote_xprt.channel);
+}
+
+static int msm_ipc_router_smd_remote_write(void *data,
+                                          uint32_t len,
+                                          uint32_t type)
+{
+       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;
+
+       if (!pkt)
+               return -EINVAL;
+
+       if (!len || pkt->length != len)
+               return -EINVAL;
+
+       align_sz = ALIGN_SIZE(pkt->length);
+
+       skb_queue_walk(pkt->pkt_fragment_q, ipc_rtr_pkt) {
+               offset = 0;
+               while (offset < ipc_rtr_pkt->len) {
+                       wait_event_interruptible_timeout(write_avail_wait_q,
+                               smd_write_avail(smd_remote_xprt.channel),
+                               msecs_to_jiffies(50));
+
+                       sz_written = smd_write(smd_remote_xprt.channel,
+                                         ipc_rtr_pkt->data + offset,
+                                         (ipc_rtr_pkt->len - offset));
+                       offset += sz_written;
+                       sz_written = 0;
+               }
+       }
+
+       if (align_sz) {
+               wait_event_interruptible_timeout(write_avail_wait_q,
+                       (smd_write_avail(smd_remote_xprt.channel) >=
+                        align_sz), msecs_to_jiffies(50));
+
+               smd_write(smd_remote_xprt.channel, &align_data, align_sz);
+       }
+       return len;
+}
+
+static int msm_ipc_router_smd_remote_close(void)
+{
+       return smd_close(smd_remote_xprt.channel);
+}
+
+static void smd_xprt_read_data(struct work_struct *work)
+{
+       int pkt_size, sz_read, sz;
+       struct sk_buff *ipc_rtr_pkt;
+       void *data;
+
+       while ((pkt_size = smd_cur_packet_size(smd_remote_xprt.channel)) &&
+               smd_read_avail(smd_remote_xprt.channel)) {
+               if (!is_partial_in_pkt) {
+                       in_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
+                       if (!in_pkt) {
+                               pr_err("%s: Couldn't alloc rr_packet\n",
+                                       __func__);
+                               return;
+                       }
+
+                       in_pkt->pkt_fragment_q = kmalloc(
+                                                 sizeof(struct sk_buff_head),
+                                                 GFP_KERNEL);
+                       if (!in_pkt->pkt_fragment_q) {
+                               pr_err("%s: Couldn't alloc pkt_fragment_q\n",
+                                       __func__);
+                               kfree(in_pkt);
+                               return;
+                       }
+                       skb_queue_head_init(in_pkt->pkt_fragment_q);
+                       is_partial_in_pkt = 1;
+               }
+
+               if ((pkt_size >= MIN_FRAG_SZ) &&
+                   (smd_read_avail(smd_remote_xprt.channel) < MIN_FRAG_SZ))
+                       return;
+
+               sz = smd_read_avail(smd_remote_xprt.channel);
+               do {
+                       ipc_rtr_pkt = alloc_skb(sz, GFP_KERNEL);
+                       if (!ipc_rtr_pkt) {
+                               if (sz <= (PAGE_SIZE/2)) {
+                                       queue_delayed_work(smd_xprt_workqueue,
+                                                  &work_read_data,
+                                                  msecs_to_jiffies(100));
+                                       return;
+                               }
+                               sz = sz / 2;
+                       }
+               } while (!ipc_rtr_pkt);
+
+               data = skb_put(ipc_rtr_pkt, sz);
+               sz_read = smd_read(smd_remote_xprt.channel, data, sz);
+               if (sz_read != sz) {
+                       pr_err("%s: Couldn't read completely\n", __func__);
+                       kfree_skb(ipc_rtr_pkt);
+                       release_pkt(in_pkt);
+                       is_partial_in_pkt = 0;
+                       return;
+               }
+               skb_queue_tail(in_pkt->pkt_fragment_q, ipc_rtr_pkt);
+               in_pkt->length += sz_read;
+               if (sz_read != pkt_size)
+                       is_partial_in_pkt = 1;
+               else
+                       is_partial_in_pkt = 0;
+
+               if (!is_partial_in_pkt) {
+                       msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
+                                          IPC_ROUTER_XPRT_EVENT_DATA,
+                                          (void *)in_pkt);
+                       release_pkt(in_pkt);
+                       in_pkt = NULL;
+               }
+       }
+}
+
+static void msm_ipc_router_smd_remote_notify(void *_dev, unsigned event)
+{
+       if (event == SMD_EVENT_DATA) {
+               if (smd_read_avail(smd_remote_xprt.channel))
+                       queue_delayed_work(smd_xprt_workqueue,
+                                          &work_read_data, 0);
+               if (smd_write_avail(smd_remote_xprt.channel))
+                       wake_up(&write_avail_wait_q);
+       }
+}
+
+static int msm_ipc_router_smd_remote_probe(struct platform_device *pdev)
+{
+       int rc;
+
+       smd_xprt_workqueue = create_singlethread_workqueue("smd_xprt");
+       if (!smd_xprt_workqueue)
+               return -ENOMEM;
+
+       smd_remote_xprt.xprt.name = "msm_ipc_router_smd_xprt";
+       smd_remote_xprt.xprt.link_id = 1;
+       smd_remote_xprt.xprt.read_avail = NULL;
+       smd_remote_xprt.xprt.read = NULL;
+       smd_remote_xprt.xprt.write_avail =
+               msm_ipc_router_smd_remote_write_avail;
+       smd_remote_xprt.xprt.write = msm_ipc_router_smd_remote_write;
+       smd_remote_xprt.xprt.close = msm_ipc_router_smd_remote_close;
+       smd_remote_xprt.xprt.priv = NULL;
+
+       init_waitqueue_head(&write_avail_wait_q);
+
+       rc = smd_open("SMD_RPCRPY_CNTL", &smd_remote_xprt.channel, NULL,
+                     msm_ipc_router_smd_remote_notify);
+       if (rc < 0) {
+               destroy_workqueue(smd_xprt_workqueue);
+               return rc;
+       }
+
+       msm_ipc_router_xprt_notify(&smd_remote_xprt.xprt,
+                                 IPC_ROUTER_XPRT_EVENT_OPEN,
+                                 NULL);
+       return 0;
+}
+
+static struct platform_driver msm_ipc_router_smd_remote_driver = {
+       .probe          = msm_ipc_router_smd_remote_probe,
+       .driver         = {
+                       .name   = "SMD_RPCRPY_CNTL",
+                       .owner  = THIS_MODULE,
+       },
+};
+
+static int __init msm_ipc_router_smd_init(void)
+{
+       return platform_driver_register(&msm_ipc_router_smd_remote_driver);
+}
+
+module_init(msm_ipc_router_smd_init);
+MODULE_DESCRIPTION("RPC Router SMD XPRT");
+MODULE_LICENSE("GPL v2");
-- 
1.7.3.3

Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
--
To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to