blob: 9b34155e7891b6d26eea43e12563732acf732cca [file] [log] [blame]
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07001/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define DEBUG
14
15#include <linux/slab.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/string.h>
19#include <linux/errno.h>
20#include <linux/init.h>
21#include <linux/types.h>
22#include <linux/delay.h>
23#include <linux/err.h>
24#include <linux/sched.h>
25#include <linux/poll.h>
26#include <linux/wakelock.h>
27#include <linux/platform_device.h>
28#include <linux/uaccess.h>
29#include <linux/debugfs.h>
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -060030#include <linux/rwsem.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include <asm/uaccess.h>
33#include <asm/byteorder.h>
34
35#include <mach/smem_log.h>
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060036#include <mach/subsystem_notif.h>
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -060037#include <mach/msm_ipc_router.h>
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060038#include <mach/msm_ipc_logging.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039
40#include "ipc_router.h"
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -060041#include "modem_notifier.h"
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -060042#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
44enum {
45 SMEM_LOG = 1U << 0,
46 RTR_DBG = 1U << 1,
47 R2R_MSG = 1U << 2,
48 R2R_RAW = 1U << 3,
49 NTFY_MSG = 1U << 4,
50 R2R_RAW_HDR = 1U << 5,
51};
52
53static int msm_ipc_router_debug_mask;
54module_param_named(debug_mask, msm_ipc_router_debug_mask,
55 int, S_IRUGO | S_IWUSR | S_IWGRP);
56
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060057static void *ipc_rtr_log_ctxt;
58#define IPC_RTR_LOG_PAGES 5
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070059#define DIAG(x...) pr_info("[RR] ERROR " x)
60
61#if defined(DEBUG)
62#define D(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060063if (ipc_rtr_log_ctxt) \
64 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065if (msm_ipc_router_debug_mask & RTR_DBG) \
66 pr_info(x); \
67} while (0)
68
69#define RR(x...) do { \
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -060070if (ipc_rtr_log_ctxt) \
71 ipc_log_string(ipc_rtr_log_ctxt, x); \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072if (msm_ipc_router_debug_mask & R2R_MSG) \
73 pr_info("[RR] "x); \
74} while (0)
75
76#define RAW(x...) do { \
77if (msm_ipc_router_debug_mask & R2R_RAW) \
78 pr_info("[RAW] "x); \
79} while (0)
80
81#define NTFY(x...) do { \
82if (msm_ipc_router_debug_mask & NTFY_MSG) \
83 pr_info("[NOTIFY] "x); \
84} while (0)
85
86#define RAW_HDR(x...) do { \
87if (msm_ipc_router_debug_mask & R2R_RAW_HDR) \
88 pr_info("[HDR] "x); \
89} while (0)
90#else
91#define D(x...) do { } while (0)
92#define RR(x...) do { } while (0)
93#define RAW(x...) do { } while (0)
94#define RAW_HDR(x...) do { } while (0)
95#define NTFY(x...) do { } while (0)
96#endif
97
Zaheerulla Meer1ee914e2013-06-19 16:31:17 +053098#define IPC_ROUTER_LOG_EVENT_ERROR 0x00
99#define IPC_ROUTER_LOG_EVENT_TX 0x01
100#define IPC_ROUTER_LOG_EVENT_RX 0x02
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +0530101#define IPC_ROUTER_DUMMY_DEST_NODE 0xFFFFFFFF
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102
103static LIST_HEAD(control_ports);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600104static DECLARE_RWSEM(control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700105
106#define LP_HASH_SIZE 32
107static struct list_head local_ports[LP_HASH_SIZE];
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600108static DECLARE_RWSEM(local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -0600110/*
111 * Server info is organized as a hash table. The server's service ID is
112 * used to index into the hash table. The instance ID of most of the servers
113 * are 1 or 2. The service IDs are well distributed compared to the instance
114 * IDs and hence choosing service ID to index into this hash table optimizes
115 * the hash table operations like add, lookup, destroy.
116 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700117#define SRV_HASH_SIZE 32
118static struct list_head server_list[SRV_HASH_SIZE];
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600119static DECLARE_RWSEM(server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120
121struct msm_ipc_server {
122 struct list_head list;
123 struct msm_ipc_port_name name;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600124 int synced_sec_rule;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600125 char pdev_name[32];
126 int next_pdev_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 struct list_head server_port_list;
128};
129
130struct msm_ipc_server_port {
131 struct list_head list;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600132 struct platform_device pdev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133 struct msm_ipc_port_addr server_addr;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600134 struct msm_ipc_router_xprt_info *xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700135};
136
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530137struct msm_ipc_resume_tx_port {
138 struct list_head list;
139 uint32_t port_id;
140 uint32_t node_id;
141};
142
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143#define RP_HASH_SIZE 32
144struct msm_ipc_router_remote_port {
145 struct list_head list;
146 uint32_t node_id;
147 uint32_t port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 uint32_t tx_quota_cnt;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600149 struct mutex quota_lock_lhb2;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530150 struct list_head resume_tx_port_list;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600151 void *sec_rule;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600152 struct msm_ipc_server *server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153};
154
155struct msm_ipc_router_xprt_info {
156 struct list_head list;
157 struct msm_ipc_router_xprt *xprt;
158 uint32_t remote_node_id;
159 uint32_t initialized;
160 struct list_head pkt_list;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 struct wake_lock wakelock;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600162 struct mutex rx_lock_lhb2;
163 struct mutex tx_lock_lhb2;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 uint32_t need_len;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600165 uint32_t abort_data_read;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 struct work_struct read_data;
167 struct workqueue_struct *workqueue;
168};
169
170#define RT_HASH_SIZE 4
171struct msm_ipc_routing_table_entry {
172 struct list_head list;
173 uint32_t node_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600174 uint32_t neighbor_node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 struct list_head remote_port_list[RP_HASH_SIZE];
176 struct msm_ipc_router_xprt_info *xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600177 struct rw_semaphore lock_lha4;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178 unsigned long num_tx_bytes;
179 unsigned long num_rx_bytes;
180};
181
182static struct list_head routing_table[RT_HASH_SIZE];
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600183static DECLARE_RWSEM(routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184static int routing_table_inited;
185
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700186static void do_read_data(struct work_struct *work);
187
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700188static LIST_HEAD(xprt_info_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600189static DECLARE_RWSEM(xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -0600191static DECLARE_COMPLETION(msm_ipc_local_router_up);
192#define IPC_ROUTER_INIT_TIMEOUT (10 * HZ)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193
194static uint32_t next_port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600195static DEFINE_MUTEX(next_port_id_lock_lha1);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600196static struct workqueue_struct *msm_ipc_router_workqueue;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197
198enum {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 DOWN,
200 UP,
201};
202
203static void init_routing_table(void)
204{
205 int i;
206 for (i = 0; i < RT_HASH_SIZE; i++)
207 INIT_LIST_HEAD(&routing_table[i]);
208}
209
210static struct msm_ipc_routing_table_entry *alloc_routing_table_entry(
211 uint32_t node_id)
212{
213 int i;
214 struct msm_ipc_routing_table_entry *rt_entry;
215
216 rt_entry = kmalloc(sizeof(struct msm_ipc_routing_table_entry),
217 GFP_KERNEL);
218 if (!rt_entry) {
219 pr_err("%s: rt_entry allocation failed for %d\n",
220 __func__, node_id);
221 return NULL;
222 }
223
224 for (i = 0; i < RP_HASH_SIZE; i++)
225 INIT_LIST_HEAD(&rt_entry->remote_port_list[i]);
226
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600227 init_rwsem(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700228 rt_entry->node_id = node_id;
229 rt_entry->xprt_info = NULL;
230 return rt_entry;
231}
232
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600233/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234static int add_routing_table_entry(
235 struct msm_ipc_routing_table_entry *rt_entry)
236{
237 uint32_t key;
238
239 if (!rt_entry)
240 return -EINVAL;
241
242 key = (rt_entry->node_id % RT_HASH_SIZE);
243 list_add_tail(&rt_entry->list, &routing_table[key]);
244 return 0;
245}
246
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600247/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248static struct msm_ipc_routing_table_entry *lookup_routing_table(
249 uint32_t node_id)
250{
251 uint32_t key = (node_id % RT_HASH_SIZE);
252 struct msm_ipc_routing_table_entry *rt_entry;
253
254 list_for_each_entry(rt_entry, &routing_table[key], list) {
255 if (rt_entry->node_id == node_id)
256 return rt_entry;
257 }
258 return NULL;
259}
260
261struct rr_packet *rr_read(struct msm_ipc_router_xprt_info *xprt_info)
262{
263 struct rr_packet *temp_pkt;
264
265 if (!xprt_info)
266 return NULL;
267
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600268 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600269 if (xprt_info->abort_data_read) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600270 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -0600271 pr_err("%s detected SSR & exiting now\n",
272 xprt_info->xprt->name);
273 return NULL;
274 }
275
276 if (list_empty(&xprt_info->pkt_list)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600277 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -0600278 return NULL;
279 }
280
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700281 temp_pkt = list_first_entry(&xprt_info->pkt_list,
282 struct rr_packet, list);
283 list_del(&temp_pkt->list);
284 if (list_empty(&xprt_info->pkt_list))
285 wake_unlock(&xprt_info->wakelock);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600286 mutex_unlock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287 return temp_pkt;
288}
289
290struct rr_packet *clone_pkt(struct rr_packet *pkt)
291{
292 struct rr_packet *cloned_pkt;
293 struct sk_buff *temp_skb, *cloned_skb;
294 struct sk_buff_head *pkt_fragment_q;
295
296 cloned_pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
297 if (!cloned_pkt) {
298 pr_err("%s: failure\n", __func__);
299 return NULL;
300 }
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600301 memcpy(&(cloned_pkt->hdr), &(pkt->hdr), sizeof(struct rr_header_v1));
302 /* TODO: Copy optional headers, if available */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303
304 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
305 if (!pkt_fragment_q) {
306 pr_err("%s: pkt_frag_q alloc failure\n", __func__);
307 kfree(cloned_pkt);
308 return NULL;
309 }
310 skb_queue_head_init(pkt_fragment_q);
311
312 skb_queue_walk(pkt->pkt_fragment_q, temp_skb) {
313 cloned_skb = skb_clone(temp_skb, GFP_KERNEL);
314 if (!cloned_skb)
315 goto fail_clone;
316 skb_queue_tail(pkt_fragment_q, cloned_skb);
317 }
318 cloned_pkt->pkt_fragment_q = pkt_fragment_q;
319 cloned_pkt->length = pkt->length;
320 return cloned_pkt;
321
322fail_clone:
323 while (!skb_queue_empty(pkt_fragment_q)) {
324 temp_skb = skb_dequeue(pkt_fragment_q);
325 kfree_skb(temp_skb);
326 }
327 kfree(pkt_fragment_q);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600328 /* TODO: Free optional headers, if present */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329 kfree(cloned_pkt);
330 return NULL;
331}
332
333struct rr_packet *create_pkt(struct sk_buff_head *data)
334{
335 struct rr_packet *pkt;
336 struct sk_buff *temp_skb;
337
338 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
339 if (!pkt) {
340 pr_err("%s: failure\n", __func__);
341 return NULL;
342 }
343
344 pkt->pkt_fragment_q = data;
345 skb_queue_walk(pkt->pkt_fragment_q, temp_skb)
346 pkt->length += temp_skb->len;
347 return pkt;
348}
349
350void release_pkt(struct rr_packet *pkt)
351{
352 struct sk_buff *temp_skb;
353
354 if (!pkt)
355 return;
356
357 if (!pkt->pkt_fragment_q) {
358 kfree(pkt);
359 return;
360 }
361
362 while (!skb_queue_empty(pkt->pkt_fragment_q)) {
363 temp_skb = skb_dequeue(pkt->pkt_fragment_q);
364 kfree_skb(temp_skb);
365 }
366 kfree(pkt->pkt_fragment_q);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600367 /* TODO: Free Optional headers, if present */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 kfree(pkt);
369 return;
370}
371
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600372static struct sk_buff_head *msm_ipc_router_buf_to_skb(void *buf,
373 unsigned int buf_len)
374{
375 struct sk_buff_head *skb_head;
376 struct sk_buff *skb;
377 int first = 1, offset = 0;
378 int skb_size, data_size;
379 void *data;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600380 int last = 1;
381 int align_size;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600382
383 skb_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
384 if (!skb_head) {
385 pr_err("%s: Couldnot allocate skb_head\n", __func__);
386 return NULL;
387 }
388 skb_queue_head_init(skb_head);
389
390 data_size = buf_len;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600391 align_size = ALIGN_SIZE(data_size);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600392 while (offset != buf_len) {
393 skb_size = data_size;
394 if (first)
395 skb_size += IPC_ROUTER_HDR_SIZE;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600396 if (last)
397 skb_size += align_size;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600398
399 skb = alloc_skb(skb_size, GFP_KERNEL);
400 if (!skb) {
401 if (skb_size <= (PAGE_SIZE/2)) {
402 pr_err("%s: cannot allocate skb\n", __func__);
403 goto buf_to_skb_error;
404 }
405 data_size = data_size / 2;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600406 last = 0;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600407 continue;
408 }
409
410 if (first) {
411 skb_reserve(skb, IPC_ROUTER_HDR_SIZE);
412 first = 0;
413 }
414
415 data = skb_put(skb, data_size);
416 memcpy(skb->data, buf + offset, data_size);
417 skb_queue_tail(skb_head, skb);
418 offset += data_size;
419 data_size = buf_len - offset;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600420 last = 1;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600421 }
422 return skb_head;
423
424buf_to_skb_error:
425 while (!skb_queue_empty(skb_head)) {
426 skb = skb_dequeue(skb_head);
427 kfree_skb(skb);
428 }
429 kfree(skb_head);
430 return NULL;
431}
432
433static void *msm_ipc_router_skb_to_buf(struct sk_buff_head *skb_head,
434 unsigned int len)
435{
436 struct sk_buff *temp;
437 int offset = 0, buf_len = 0, copy_len;
438 void *buf;
439
440 if (!skb_head) {
441 pr_err("%s: NULL skb_head\n", __func__);
442 return NULL;
443 }
444
445 temp = skb_peek(skb_head);
446 buf_len = len;
447 buf = kmalloc(buf_len, GFP_KERNEL);
448 if (!buf) {
449 pr_err("%s: cannot allocate buf\n", __func__);
450 return NULL;
451 }
452 skb_queue_walk(skb_head, temp) {
453 copy_len = buf_len < temp->len ? buf_len : temp->len;
454 memcpy(buf + offset, temp->data, copy_len);
455 offset += copy_len;
456 buf_len -= copy_len;
457 }
458 return buf;
459}
460
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +0530461void msm_ipc_router_free_skb(struct sk_buff_head *skb_head)
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600462{
463 struct sk_buff *temp_skb;
464
465 if (!skb_head)
466 return;
467
468 while (!skb_queue_empty(skb_head)) {
469 temp_skb = skb_dequeue(skb_head);
470 kfree_skb(temp_skb);
471 }
472 kfree(skb_head);
473}
474
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600475/**
476 * extract_header_v1() - Extract IPC Router header of version 1
477 * @pkt: Packet structure into which the header has to be extraced.
478 * @skb: SKB from which the header has to be extracted.
479 *
480 * @return: 0 on success, standard Linux error codes on failure.
481 */
482static int extract_header_v1(struct rr_packet *pkt, struct sk_buff *skb)
483{
484 if (!pkt || !skb) {
485 pr_err("%s: Invalid pkt or skb\n", __func__);
486 return -EINVAL;
487 }
488
489 memcpy(&pkt->hdr, skb->data, sizeof(struct rr_header_v1));
490 skb_pull(skb, sizeof(struct rr_header_v1));
491 pkt->length -= sizeof(struct rr_header_v1);
492 return 0;
493}
494
495/**
496 * extract_header_v2() - Extract IPC Router header of version 2
497 * @pkt: Packet structure into which the header has to be extraced.
498 * @skb: SKB from which the header has to be extracted.
499 *
500 * @return: 0 on success, standard Linux error codes on failure.
501 */
502static int extract_header_v2(struct rr_packet *pkt, struct sk_buff *skb)
503{
504 struct rr_header_v2 *hdr;
505
506 if (!pkt || !skb) {
507 pr_err("%s: Invalid pkt or skb\n", __func__);
508 return -EINVAL;
509 }
510
511 hdr = (struct rr_header_v2 *)skb->data;
512 pkt->hdr.version = (uint32_t)hdr->version;
513 pkt->hdr.type = (uint32_t)hdr->type;
514 pkt->hdr.src_node_id = (uint32_t)hdr->src_node_id;
515 pkt->hdr.src_port_id = (uint32_t)hdr->src_port_id;
516 pkt->hdr.size = (uint32_t)hdr->size;
517 pkt->hdr.control_flag = (uint32_t)hdr->control_flag;
518 pkt->hdr.dst_node_id = (uint32_t)hdr->dst_node_id;
519 pkt->hdr.dst_port_id = (uint32_t)hdr->dst_port_id;
520 skb_pull(skb, sizeof(struct rr_header_v2));
521 pkt->length -= sizeof(struct rr_header_v2);
522 return 0;
523}
524
525/**
526 * extract_header() - Extract IPC Router header
527 * @pkt: Packet from which the header has to be extraced.
528 *
529 * @return: 0 on success, standard Linux error codes on failure.
530 *
531 * This function will check if the header version is v1 or v2 and invoke
532 * the corresponding helper function to extract the IPC Router header.
533 */
534static int extract_header(struct rr_packet *pkt)
535{
536 struct sk_buff *temp_skb;
537 int ret;
538
539 if (!pkt) {
540 pr_err("%s: NULL PKT\n", __func__);
541 return -EINVAL;
542 }
543
544 temp_skb = skb_peek(pkt->pkt_fragment_q);
545 if (!temp_skb || !temp_skb->data) {
546 pr_err("%s: No SKBs in skb_queue\n", __func__);
547 return -EINVAL;
548 }
549
550 if (temp_skb->data[0] == IPC_ROUTER_V1) {
551 ret = extract_header_v1(pkt, temp_skb);
552 } else if (temp_skb->data[0] == IPC_ROUTER_V2) {
553 ret = extract_header_v2(pkt, temp_skb);
554 /* TODO: Extract optional headers if present */
555 } else {
556 pr_err("%s: Invalid Header version %02x\n",
557 __func__, temp_skb->data[0]);
558 print_hex_dump(KERN_ERR, "Header: ", DUMP_PREFIX_ADDRESS,
559 16, 1, temp_skb->data, pkt->length, true);
560 return -EINVAL;
561 }
562 return ret;
563}
564
565/**
566 * calc_tx_header_size() - Calculate header size to be reserved in SKB
567 * @pkt: Packet in which the space for header has to be reserved.
568 * @dst_xprt_info: XPRT through which the destination is reachable.
569 *
570 * @return: required header size on success,
571 * starndard Linux error codes on failure.
572 *
573 * This function is used to calculate the header size that has to be reserved
574 * in a transmit SKB. The header size is calculated based on the XPRT through
575 * which the destination node is reachable.
576 */
577static int calc_tx_header_size(struct rr_packet *pkt,
578 struct msm_ipc_router_xprt_info *dst_xprt_info)
579{
580 int hdr_size = 0;
581 int xprt_version = 0;
582 struct msm_ipc_routing_table_entry *rt_entry;
583 struct msm_ipc_router_xprt_info *xprt_info = dst_xprt_info;
584
585 if (!pkt) {
586 pr_err("%s: NULL PKT\n", __func__);
587 return -EINVAL;
588 }
589
590 if (!xprt_info) {
591 rt_entry = lookup_routing_table(pkt->hdr.dst_node_id);
592 if (!rt_entry || !(rt_entry->xprt_info)) {
593 pr_err("%s: Node %d is not up\n",
594 __func__, pkt->hdr.dst_node_id);
595 return -ENODEV;
596 }
597
598 xprt_info = rt_entry->xprt_info;
599 }
600 if (xprt_info)
601 xprt_version = xprt_info->xprt->get_version(xprt_info->xprt);
602
603 if (xprt_version == IPC_ROUTER_V1) {
604 pkt->hdr.version = IPC_ROUTER_V1;
605 hdr_size = sizeof(struct rr_header_v1);
606 } else if (xprt_version == IPC_ROUTER_V2) {
607 pkt->hdr.version = IPC_ROUTER_V2;
608 hdr_size = sizeof(struct rr_header_v2);
609 /* TODO: Calculate optional header length, if present */
610 } else {
611 pr_err("%s: Invalid xprt_version %d\n",
612 __func__, xprt_version);
613 hdr_size = -EINVAL;
614 }
615
616 return hdr_size;
617}
618
619/**
Karthikeyan Ramasubramanian94337602013-09-17 19:31:59 -0600620 * calc_rx_header_size() - Calculate the RX header size
621 * @xprt_info: XPRT info of the received message.
622 *
623 * @return: valid header size on success, INT_MAX on failure.
624 */
625static int calc_rx_header_size(struct msm_ipc_router_xprt_info *xprt_info)
626{
627 int xprt_version = 0;
628 int hdr_size = INT_MAX;
629
630 if (xprt_info)
631 xprt_version = xprt_info->xprt->get_version(xprt_info->xprt);
632
633 if (xprt_version == IPC_ROUTER_V1)
634 hdr_size = sizeof(struct rr_header_v1);
635 else if (xprt_version == IPC_ROUTER_V2)
636 hdr_size = sizeof(struct rr_header_v2);
637 return hdr_size;
638}
639
640/**
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600641 * prepend_header_v1() - Prepend IPC Router header of version 1
642 * @pkt: Packet structure which contains the header info to be prepended.
643 * @hdr_size: Size of the header
644 *
645 * @return: 0 on success, standard Linux error codes on failure.
646 */
647static int prepend_header_v1(struct rr_packet *pkt, int hdr_size)
648{
649 struct sk_buff *temp_skb;
650 struct rr_header_v1 *hdr;
651
652 if (!pkt || hdr_size <= 0) {
653 pr_err("%s: Invalid input parameters\n", __func__);
654 return -EINVAL;
655 }
656
657 temp_skb = skb_peek(pkt->pkt_fragment_q);
658 if (!temp_skb || !temp_skb->data) {
659 pr_err("%s: No SKBs in skb_queue\n", __func__);
660 return -EINVAL;
661 }
662
663 if (skb_headroom(temp_skb) < hdr_size) {
664 temp_skb = alloc_skb(hdr_size, GFP_KERNEL);
665 if (!temp_skb) {
666 pr_err("%s: Could not allocate SKB of size %d\n",
667 __func__, hdr_size);
668 return -ENOMEM;
669 }
670 }
671
672 hdr = (struct rr_header_v1 *)skb_push(temp_skb, hdr_size);
673 memcpy(hdr, &pkt->hdr, hdr_size);
674 if (temp_skb != skb_peek(pkt->pkt_fragment_q))
675 skb_queue_head(pkt->pkt_fragment_q, temp_skb);
676 pkt->length += hdr_size;
677 return 0;
678}
679
680/**
681 * prepend_header_v2() - Prepend IPC Router header of version 2
682 * @pkt: Packet structure which contains the header info to be prepended.
683 * @hdr_size: Size of the header
684 *
685 * @return: 0 on success, standard Linux error codes on failure.
686 */
687static int prepend_header_v2(struct rr_packet *pkt, int hdr_size)
688{
689 struct sk_buff *temp_skb;
690 struct rr_header_v2 *hdr;
691
692 if (!pkt || hdr_size <= 0) {
693 pr_err("%s: Invalid input parameters\n", __func__);
694 return -EINVAL;
695 }
696
697 temp_skb = skb_peek(pkt->pkt_fragment_q);
698 if (!temp_skb || !temp_skb->data) {
699 pr_err("%s: No SKBs in skb_queue\n", __func__);
700 return -EINVAL;
701 }
702
703 if (skb_headroom(temp_skb) < hdr_size) {
704 temp_skb = alloc_skb(hdr_size, GFP_KERNEL);
705 if (!temp_skb) {
706 pr_err("%s: Could not allocate SKB of size %d\n",
707 __func__, hdr_size);
708 return -ENOMEM;
709 }
710 }
711
712 hdr = (struct rr_header_v2 *)skb_push(temp_skb, hdr_size);
713 hdr->version = (uint8_t)pkt->hdr.version;
714 hdr->type = (uint8_t)pkt->hdr.type;
715 hdr->control_flag = (uint16_t)pkt->hdr.control_flag;
716 hdr->size = (uint32_t)pkt->hdr.size;
717 hdr->src_node_id = (uint16_t)pkt->hdr.src_node_id;
718 hdr->src_port_id = (uint16_t)pkt->hdr.src_port_id;
719 hdr->dst_node_id = (uint16_t)pkt->hdr.dst_node_id;
720 hdr->dst_port_id = (uint16_t)pkt->hdr.dst_port_id;
721 /* TODO: Add optional headers, if present */
722 if (temp_skb != skb_peek(pkt->pkt_fragment_q))
723 skb_queue_head(pkt->pkt_fragment_q, temp_skb);
724 pkt->length += hdr_size;
725 return 0;
726}
727
728/**
729 * prepend_header() - Prepend IPC Router header
730 * @pkt: Packet structure which contains the header info to be prepended.
731 * @xprt_info: XPRT through which the packet is transmitted.
732 *
733 * @return: 0 on success, standard Linux error codes on failure.
734 *
735 * This function prepends the header to the packet to be transmitted. The
736 * IPC Router header version to be prepended depends on the XPRT through
737 * which the destination is reachable.
738 */
739static int prepend_header(struct rr_packet *pkt,
740 struct msm_ipc_router_xprt_info *xprt_info)
741{
742 int hdr_size;
743 struct sk_buff *temp_skb;
744
745 if (!pkt) {
746 pr_err("%s: NULL PKT\n", __func__);
747 return -EINVAL;
748 }
749
750 temp_skb = skb_peek(pkt->pkt_fragment_q);
751 if (!temp_skb || !temp_skb->data) {
752 pr_err("%s: No SKBs in skb_queue\n", __func__);
753 return -EINVAL;
754 }
755
756 hdr_size = calc_tx_header_size(pkt, xprt_info);
757 if (hdr_size <= 0)
758 return hdr_size;
759
760 if (pkt->hdr.version == IPC_ROUTER_V1)
761 return prepend_header_v1(pkt, hdr_size);
762 else if (pkt->hdr.version == IPC_ROUTER_V2)
763 return prepend_header_v2(pkt, hdr_size);
764 else
765 return -EINVAL;
766}
767
768/**
769 * defragment_pkt() - Defragment and linearize the packet
770 * @pkt: Packet to be linearized.
771 *
772 * @return: 0 on success, standard Linux error codes on failure.
773 *
774 * Some packets contain fragments of data over multiple SKBs. If an XPRT
775 * does not supported fragmented writes, linearize multiple SKBs into one
776 * single SKB.
777 */
778static int defragment_pkt(struct rr_packet *pkt)
779{
780 struct sk_buff *dst_skb, *src_skb, *temp_skb;
781 int offset = 0, buf_len = 0, copy_len;
782 void *buf;
783 int align_size;
784
785 if (!pkt || pkt->length <= 0) {
786 pr_err("%s: Invalid PKT\n", __func__);
787 return -EINVAL;
788 }
789
790 if (skb_queue_len(pkt->pkt_fragment_q) == 1)
791 return 0;
792
793 align_size = ALIGN_SIZE(pkt->length);
794 dst_skb = alloc_skb(pkt->length + align_size, GFP_KERNEL);
795 if (!dst_skb) {
796 pr_err("%s: could not allocate one skb of size %d\n",
797 __func__, pkt->length);
798 return -ENOMEM;
799 }
800 buf = skb_put(dst_skb, pkt->length);
801 buf_len = pkt->length;
802
803 skb_queue_walk(pkt->pkt_fragment_q, src_skb) {
804 copy_len = buf_len < src_skb->len ? buf_len : src_skb->len;
805 memcpy(buf + offset, src_skb->data, copy_len);
806 offset += copy_len;
807 buf_len -= copy_len;
808 }
809
810 while (!skb_queue_empty(pkt->pkt_fragment_q)) {
811 temp_skb = skb_dequeue(pkt->pkt_fragment_q);
812 kfree_skb(temp_skb);
813 }
814 skb_queue_tail(pkt->pkt_fragment_q, dst_skb);
815 return 0;
816}
817
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600818static int post_pkt_to_port(struct msm_ipc_port *port_ptr,
819 struct rr_packet *pkt, int clone)
820{
821 struct rr_packet *temp_pkt = pkt;
Zaheerulla Meer29302cf2013-05-24 21:19:18 +0530822 void (*notify)(unsigned event, void *priv);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600823
824 if (unlikely(!port_ptr || !pkt))
825 return -EINVAL;
826
827 if (clone) {
828 temp_pkt = clone_pkt(pkt);
829 if (!temp_pkt) {
830 pr_err("%s: Error cloning packet for port %08x:%08x\n",
831 __func__, port_ptr->this_port.node_id,
832 port_ptr->this_port.port_id);
833 return -ENOMEM;
834 }
835 }
836
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600837 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600838 wake_lock(&port_ptr->port_rx_wake_lock);
839 list_add_tail(&temp_pkt->list, &port_ptr->port_rx_q);
840 wake_up(&port_ptr->port_rx_wait_q);
Zaheerulla Meer29302cf2013-05-24 21:19:18 +0530841 notify = port_ptr->notify;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600842 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Zaheerulla Meer29302cf2013-05-24 21:19:18 +0530843 if (notify)
844 notify(MSM_IPC_ROUTER_READ_CB, port_ptr->priv);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600845 return 0;
846}
847
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700848static int post_control_ports(struct rr_packet *pkt)
849{
850 struct msm_ipc_port *port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700851
852 if (!pkt)
853 return -EINVAL;
854
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600855 down_read(&control_ports_lock_lha5);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -0600856 list_for_each_entry(port_ptr, &control_ports, list)
857 post_pkt_to_port(port_ptr, pkt, 1);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600858 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700859 return 0;
860}
861
862static uint32_t allocate_port_id(void)
863{
864 uint32_t port_id = 0, prev_port_id, key;
865 struct msm_ipc_port *port_ptr;
866
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600867 mutex_lock(&next_port_id_lock_lha1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700868 prev_port_id = next_port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600869 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700870 do {
871 next_port_id++;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -0600872 if ((next_port_id & IPC_ROUTER_ADDRESS) == IPC_ROUTER_ADDRESS)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700873 next_port_id = 1;
874
875 key = (next_port_id & (LP_HASH_SIZE - 1));
876 if (list_empty(&local_ports[key])) {
877 port_id = next_port_id;
878 break;
879 }
880 list_for_each_entry(port_ptr, &local_ports[key], list) {
881 if (port_ptr->this_port.port_id == next_port_id) {
882 port_id = next_port_id;
883 break;
884 }
885 }
886 if (!port_id) {
887 port_id = next_port_id;
888 break;
889 }
890 port_id = 0;
891 } while (next_port_id != prev_port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600892 up_read(&local_ports_lock_lha2);
893 mutex_unlock(&next_port_id_lock_lha1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700894
895 return port_id;
896}
897
898void msm_ipc_router_add_local_port(struct msm_ipc_port *port_ptr)
899{
900 uint32_t key;
901
902 if (!port_ptr)
903 return;
904
905 key = (port_ptr->this_port.port_id & (LP_HASH_SIZE - 1));
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600906 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700907 list_add_tail(&port_ptr->list, &local_ports[key]);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600908 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700909}
910
911struct msm_ipc_port *msm_ipc_router_create_raw_port(void *endpoint,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -0600912 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700913 void *priv)
914{
915 struct msm_ipc_port *port_ptr;
916
917 port_ptr = kzalloc(sizeof(struct msm_ipc_port), GFP_KERNEL);
918 if (!port_ptr)
919 return NULL;
920
921 port_ptr->this_port.node_id = IPC_ROUTER_NID_LOCAL;
922 port_ptr->this_port.port_id = allocate_port_id();
923 if (!port_ptr->this_port.port_id) {
924 pr_err("%s: All port ids are in use\n", __func__);
925 kfree(port_ptr);
926 return NULL;
927 }
928
929 spin_lock_init(&port_ptr->port_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930 INIT_LIST_HEAD(&port_ptr->port_rx_q);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600931 mutex_init(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700932 init_waitqueue_head(&port_ptr->port_rx_wait_q);
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600933 snprintf(port_ptr->rx_wakelock_name, MAX_WAKELOCK_NAME_SZ,
Karthikeyan Ramasubramanian090486e2013-02-14 13:53:20 -0700934 "ipc%08x_%s",
935 port_ptr->this_port.port_id,
936 current->comm);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700937 wake_lock_init(&port_ptr->port_rx_wake_lock,
Karthikeyan Ramasubramanianb4aeeb92012-03-13 12:31:43 -0600938 WAKE_LOCK_SUSPEND, port_ptr->rx_wakelock_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939
940 port_ptr->endpoint = endpoint;
941 port_ptr->notify = notify;
942 port_ptr->priv = priv;
943
944 msm_ipc_router_add_local_port(port_ptr);
945 return port_ptr;
946}
947
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600948/* Must be called with local_ports_lock_lha2 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700949static struct msm_ipc_port *msm_ipc_router_lookup_local_port(uint32_t port_id)
950{
951 int key = (port_id & (LP_HASH_SIZE - 1));
952 struct msm_ipc_port *port_ptr;
953
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700954 list_for_each_entry(port_ptr, &local_ports[key], list) {
955 if (port_ptr->this_port.port_id == port_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700956 return port_ptr;
957 }
958 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700959 return NULL;
960}
961
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600962/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700963static struct msm_ipc_router_remote_port *msm_ipc_router_lookup_remote_port(
964 uint32_t node_id,
965 uint32_t port_id)
966{
967 struct msm_ipc_router_remote_port *rport_ptr;
968 struct msm_ipc_routing_table_entry *rt_entry;
969 int key = (port_id & (RP_HASH_SIZE - 1));
970
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700971 rt_entry = lookup_routing_table(node_id);
972 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700973 pr_err("%s: Node is not up\n", __func__);
974 return NULL;
975 }
976
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600977 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700978 list_for_each_entry(rport_ptr,
979 &rt_entry->remote_port_list[key], list) {
980 if (rport_ptr->port_id == port_id) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600981 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982 return rport_ptr;
983 }
984 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600985 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700986 return NULL;
987}
988
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600989/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700990static struct msm_ipc_router_remote_port *msm_ipc_router_create_remote_port(
991 uint32_t node_id,
992 uint32_t port_id)
993{
994 struct msm_ipc_router_remote_port *rport_ptr;
995 struct msm_ipc_routing_table_entry *rt_entry;
996 int key = (port_id & (RP_HASH_SIZE - 1));
997
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998 rt_entry = lookup_routing_table(node_id);
999 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001000 pr_err("%s: Node is not up\n", __func__);
1001 return NULL;
1002 }
1003
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001004 rport_ptr = kmalloc(sizeof(struct msm_ipc_router_remote_port),
1005 GFP_KERNEL);
1006 if (!rport_ptr) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001007 pr_err("%s: Remote port alloc failed\n", __func__);
1008 return NULL;
1009 }
1010 rport_ptr->port_id = port_id;
1011 rport_ptr->node_id = node_id;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001012 rport_ptr->sec_rule = NULL;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001013 rport_ptr->server = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001014 rport_ptr->tx_quota_cnt = 0;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001015 mutex_init(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301016 INIT_LIST_HEAD(&rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001017 down_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001018 list_add_tail(&rport_ptr->list,
1019 &rt_entry->remote_port_list[key]);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001020 up_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001021 return rport_ptr;
1022}
1023
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301024/**
1025 * msm_ipc_router_free_resume_tx_port() - Free the resume_tx ports
1026 * @rport_ptr: Pointer to the remote port.
1027 *
1028 * This function deletes all the resume_tx ports associated with a remote port
1029 * and frees the memory allocated to each resume_tx port.
1030 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001031 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301032 */
1033static void msm_ipc_router_free_resume_tx_port(
1034 struct msm_ipc_router_remote_port *rport_ptr)
1035{
1036 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
1037
1038 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
1039 &rport_ptr->resume_tx_port_list, list) {
1040 list_del(&rtx_port->list);
1041 kfree(rtx_port);
1042 }
1043}
1044
1045/**
1046 * msm_ipc_router_lookup_resume_tx_port() - Lookup resume_tx port list
1047 * @rport_ptr: Remote port whose resume_tx port list needs to be looked.
1048 * @port_id: Port ID which needs to be looked from the list.
1049 *
1050 * return 1 if the port_id is found in the list, else 0.
1051 *
1052 * This function is used to lookup the existence of a local port in
1053 * remote port's resume_tx list. This function is used to ensure that
1054 * the same port is not added to the remote_port's resume_tx list repeatedly.
1055 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001056 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301057 */
1058static int msm_ipc_router_lookup_resume_tx_port(
1059 struct msm_ipc_router_remote_port *rport_ptr, uint32_t port_id)
1060{
1061 struct msm_ipc_resume_tx_port *rtx_port;
1062
1063 list_for_each_entry(rtx_port, &rport_ptr->resume_tx_port_list, list) {
1064 if (port_id == rtx_port->port_id)
1065 return 1;
1066 }
1067 return 0;
1068}
1069
1070/**
1071 * post_resume_tx() - Post the resume_tx event
1072 * @rport_ptr: Pointer to the remote port
1073 * @pkt : The data packet that is received on a resume_tx event
1074 *
1075 * This function informs about the reception of the resume_tx message from a
1076 * remote port pointed by rport_ptr to all the local ports that are in the
1077 * resume_tx_ports_list of this remote port. On posting the information, this
1078 * function sequentially deletes each entry in the resume_tx_port_list of the
1079 * remote port.
1080 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001081 * Must be called with rport_ptr->quota_lock_lhb2 locked.
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301082 */
1083static void post_resume_tx(struct msm_ipc_router_remote_port *rport_ptr,
1084 struct rr_packet *pkt)
1085{
1086 struct msm_ipc_resume_tx_port *rtx_port, *tmp_rtx_port;
1087 struct msm_ipc_port *local_port;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301088
1089 list_for_each_entry_safe(rtx_port, tmp_rtx_port,
1090 &rport_ptr->resume_tx_port_list, list) {
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301091 local_port =
1092 msm_ipc_router_lookup_local_port(rtx_port->port_id);
Zaheerulla Meerc8400402013-05-08 19:27:27 +05301093 if (local_port && local_port->notify)
1094 local_port->notify(MSM_IPC_ROUTER_RESUME_TX,
1095 local_port->priv);
1096 else if (local_port)
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06001097 post_pkt_to_port(local_port, pkt, 1);
Zaheerulla Meerc8400402013-05-08 19:27:27 +05301098 else
1099 pr_err("%s: Local Port %d not Found",
1100 __func__, rtx_port->port_id);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05301101 list_del(&rtx_port->list);
1102 kfree(rtx_port);
1103 }
1104}
1105
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001106/* Must be called with routing_table_lock_lha3 locked. */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107static void msm_ipc_router_destroy_remote_port(
1108 struct msm_ipc_router_remote_port *rport_ptr)
1109{
1110 uint32_t node_id;
1111 struct msm_ipc_routing_table_entry *rt_entry;
1112
1113 if (!rport_ptr)
1114 return;
1115
1116 node_id = rport_ptr->node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001117 rt_entry = lookup_routing_table(node_id);
1118 if (!rt_entry) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001119 pr_err("%s: Node %d is not up\n", __func__, node_id);
1120 return;
1121 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001122 down_write(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001123 list_del(&rport_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001124 up_write(&rt_entry->lock_lha4);
1125 mutex_lock(&rport_ptr->quota_lock_lhb2);
1126 msm_ipc_router_free_resume_tx_port(rport_ptr);
1127 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001128 kfree(rport_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001129 return;
1130}
1131
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001132/**
1133 * msm_ipc_router_lookup_server() - Lookup server information
1134 * @service: Service ID of the server info to be looked up.
1135 * @instance: Instance ID of the server info to be looked up.
1136 * @node_id: Node/Processor ID in which the server is hosted.
1137 * @port_id: Port ID within the node in which the server is hosted.
1138 *
1139 * @return: If found Pointer to server structure, else NULL.
1140 *
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001141 * Note1: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001142 * Note2: If the <node_id:port_id> are <0:0>, then the lookup is restricted
1143 * to <service:instance>. Used only when a client wants to send a
1144 * message to any QMI server.
1145 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001146static struct msm_ipc_server *msm_ipc_router_lookup_server(
1147 uint32_t service,
1148 uint32_t instance,
1149 uint32_t node_id,
1150 uint32_t port_id)
1151{
1152 struct msm_ipc_server *server;
1153 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001154 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001155
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001156 list_for_each_entry(server, &server_list[key], list) {
1157 if ((server->name.service != service) ||
1158 (server->name.instance != instance))
1159 continue;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001160 if ((node_id == 0) && (port_id == 0))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001161 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001162 list_for_each_entry(server_port, &server->server_port_list,
1163 list) {
1164 if ((server_port->server_addr.node_id == node_id) &&
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001165 (server_port->server_addr.port_id == port_id))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001166 return server;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001167 }
1168 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001169 return NULL;
1170}
1171
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001172static void dummy_release(struct device *dev)
1173{
1174}
1175
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001176/**
1177 * msm_ipc_router_create_server() - Add server info to hash table
1178 * @service: Service ID of the server info to be created.
1179 * @instance: Instance ID of the server info to be created.
1180 * @node_id: Node/Processor ID in which the server is hosted.
1181 * @port_id: Port ID within the node in which the server is hosted.
1182 * @xprt_info: XPRT through which the node hosting the server is reached.
1183 *
1184 * @return: Pointer to server structure on success, else NULL.
1185 *
1186 * This function adds the server info to the hash table. If the same
1187 * server(i.e. <service_id:instance_id>) is hosted in different nodes,
1188 * they are maintained as list of "server_port" under "server" structure.
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001189 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001190 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001191static struct msm_ipc_server *msm_ipc_router_create_server(
1192 uint32_t service,
1193 uint32_t instance,
1194 uint32_t node_id,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001195 uint32_t port_id,
1196 struct msm_ipc_router_xprt_info *xprt_info)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001197{
1198 struct msm_ipc_server *server = NULL;
1199 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001200 int key = (service & (SRV_HASH_SIZE - 1));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001201
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001202 list_for_each_entry(server, &server_list[key], list) {
1203 if ((server->name.service == service) &&
1204 (server->name.instance == instance))
1205 goto create_srv_port;
1206 }
1207
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001208 server = kzalloc(sizeof(struct msm_ipc_server), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001209 if (!server) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001210 pr_err("%s: Server allocation failed\n", __func__);
1211 return NULL;
1212 }
1213 server->name.service = service;
1214 server->name.instance = instance;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001215 server->synced_sec_rule = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001216 INIT_LIST_HEAD(&server->server_port_list);
1217 list_add_tail(&server->list, &server_list[key]);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001218 scnprintf(server->pdev_name, sizeof(server->pdev_name),
1219 "QMI%08x:%08x", service, instance);
1220 server->next_pdev_id = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001221
1222create_srv_port:
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001223 server_port = kzalloc(sizeof(struct msm_ipc_server_port), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001224 if (!server_port) {
1225 if (list_empty(&server->server_port_list)) {
1226 list_del(&server->list);
1227 kfree(server);
1228 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001229 pr_err("%s: Server Port allocation failed\n", __func__);
1230 return NULL;
1231 }
1232 server_port->server_addr.node_id = node_id;
1233 server_port->server_addr.port_id = port_id;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001234 server_port->xprt_info = xprt_info;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001235 list_add_tail(&server_port->list, &server->server_port_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001236
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001237 server_port->pdev.name = server->pdev_name;
1238 server_port->pdev.id = server->next_pdev_id++;
1239 server_port->pdev.dev.release = dummy_release;
1240 platform_device_register(&server_port->pdev);
1241
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001242 return server;
1243}
1244
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001245/**
1246 * msm_ipc_router_destroy_server() - Remove server info from hash table
1247 * @server: Server info to be removed.
1248 * @node_id: Node/Processor ID in which the server is hosted.
1249 * @port_id: Port ID within the node in which the server is hosted.
1250 *
1251 * This function removes the server_port identified using <node_id:port_id>
1252 * from the server structure. If the server_port list under server structure
1253 * is empty after removal, then remove the server structure from the server
1254 * hash table.
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001255 * Note: Lock the server_list_lock_lha2 before accessing this function.
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06001256 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257static void msm_ipc_router_destroy_server(struct msm_ipc_server *server,
1258 uint32_t node_id, uint32_t port_id)
1259{
1260 struct msm_ipc_server_port *server_port;
1261
1262 if (!server)
1263 return;
1264
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001265 list_for_each_entry(server_port, &server->server_port_list, list) {
1266 if ((server_port->server_addr.node_id == node_id) &&
1267 (server_port->server_addr.port_id == port_id))
1268 break;
1269 }
1270 if (server_port) {
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06001271 platform_device_unregister(&server_port->pdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001272 list_del(&server_port->list);
1273 kfree(server_port);
1274 }
1275 if (list_empty(&server->server_port_list)) {
1276 list_del(&server->list);
1277 kfree(server);
1278 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001279 return;
1280}
1281
1282static int msm_ipc_router_send_control_msg(
1283 struct msm_ipc_router_xprt_info *xprt_info,
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301284 union rr_control_msg *msg,
1285 uint32_t dst_node_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001286{
1287 struct rr_packet *pkt;
1288 struct sk_buff *ipc_rtr_pkt;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001289 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001290 int pkt_size;
1291 void *data;
1292 struct sk_buff_head *pkt_fragment_q;
1293 int ret;
1294
1295 if (!xprt_info || ((msg->cmd != IPC_ROUTER_CTRL_CMD_HELLO) &&
1296 !xprt_info->initialized)) {
1297 pr_err("%s: xprt_info not initialized\n", __func__);
1298 return -EINVAL;
1299 }
1300
1301 if (xprt_info->remote_node_id == IPC_ROUTER_NID_LOCAL)
1302 return 0;
1303
1304 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
1305 if (!pkt) {
1306 pr_err("%s: pkt alloc failed\n", __func__);
1307 return -ENOMEM;
1308 }
1309
1310 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
1311 if (!pkt_fragment_q) {
1312 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
1313 kfree(pkt);
1314 return -ENOMEM;
1315 }
1316 skb_queue_head_init(pkt_fragment_q);
1317
1318 pkt_size = IPC_ROUTER_HDR_SIZE + sizeof(*msg);
1319 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
1320 if (!ipc_rtr_pkt) {
1321 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
1322 kfree(pkt_fragment_q);
1323 kfree(pkt);
1324 return -ENOMEM;
1325 }
1326
1327 skb_reserve(ipc_rtr_pkt, IPC_ROUTER_HDR_SIZE);
1328 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
1329 memcpy(data, msg, sizeof(*msg));
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001330 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1331 pkt->pkt_fragment_q = pkt_fragment_q;
1332 pkt->length = sizeof(*msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001333
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001334 hdr = &(pkt->hdr);
1335 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001336 hdr->type = msg->cmd;
1337 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1338 hdr->src_port_id = IPC_ROUTER_ADDRESS;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001339 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001340 hdr->size = sizeof(*msg);
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301341 if (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)
1342 hdr->dst_node_id = dst_node_id;
1343 else
1344 hdr->dst_node_id = xprt_info->remote_node_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001345 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001346
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001347 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001348 ret = prepend_header(pkt, xprt_info);
1349 if (ret < 0) {
1350 mutex_unlock(&xprt_info->tx_lock_lhb2);
1351 pr_err("%s: Prepend Header failed\n", __func__);
1352 release_pkt(pkt);
1353 return ret;
1354 }
1355
1356 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001357 mutex_unlock(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001358
1359 release_pkt(pkt);
1360 return ret;
1361}
1362
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001363static int msm_ipc_router_send_server_list(uint32_t node_id,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001364 struct msm_ipc_router_xprt_info *xprt_info)
1365{
1366 union rr_control_msg ctl;
1367 struct msm_ipc_server *server;
1368 struct msm_ipc_server_port *server_port;
1369 int i;
1370
1371 if (!xprt_info || !xprt_info->initialized) {
1372 pr_err("%s: Xprt info not initialized\n", __func__);
1373 return -EINVAL;
1374 }
1375
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06001376 memset(&ctl, 0, sizeof(ctl));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001377 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
1378
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001379 for (i = 0; i < SRV_HASH_SIZE; i++) {
1380 list_for_each_entry(server, &server_list[i], list) {
1381 ctl.srv.service = server->name.service;
1382 ctl.srv.instance = server->name.instance;
1383 list_for_each_entry(server_port,
1384 &server->server_port_list, list) {
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001385 if (server_port->server_addr.node_id !=
1386 node_id)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001387 continue;
1388
1389 ctl.srv.node_id =
1390 server_port->server_addr.node_id;
1391 ctl.srv.port_id =
1392 server_port->server_addr.port_id;
1393 msm_ipc_router_send_control_msg(xprt_info,
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301394 &ctl, IPC_ROUTER_DUMMY_DEST_NODE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001395 }
1396 }
1397 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001398
1399 return 0;
1400}
1401
1402#if defined(DEBUG)
1403static char *type_to_str(int i)
1404{
1405 switch (i) {
1406 case IPC_ROUTER_CTRL_CMD_DATA:
1407 return "data ";
1408 case IPC_ROUTER_CTRL_CMD_HELLO:
1409 return "hello ";
1410 case IPC_ROUTER_CTRL_CMD_BYE:
1411 return "bye ";
1412 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
1413 return "new_srvr";
1414 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
1415 return "rmv_srvr";
1416 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
1417 return "rmv_clnt";
1418 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
1419 return "resum_tx";
1420 case IPC_ROUTER_CTRL_CMD_EXIT:
1421 return "cmd_exit";
1422 default:
1423 return "invalid";
1424 }
1425}
1426#endif
1427
1428static int broadcast_ctl_msg_locally(union rr_control_msg *msg)
1429{
1430 struct rr_packet *pkt;
1431 struct sk_buff *ipc_rtr_pkt;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001432 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001433 int pkt_size;
1434 void *data;
1435 struct sk_buff_head *pkt_fragment_q;
1436 int ret;
1437
1438 pkt = kzalloc(sizeof(struct rr_packet), GFP_KERNEL);
1439 if (!pkt) {
1440 pr_err("%s: pkt alloc failed\n", __func__);
1441 return -ENOMEM;
1442 }
1443
1444 pkt_fragment_q = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
1445 if (!pkt_fragment_q) {
1446 pr_err("%s: pkt_fragment_q alloc failed\n", __func__);
1447 kfree(pkt);
1448 return -ENOMEM;
1449 }
1450 skb_queue_head_init(pkt_fragment_q);
1451
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001452 pkt_size = sizeof(*msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001453 ipc_rtr_pkt = alloc_skb(pkt_size, GFP_KERNEL);
1454 if (!ipc_rtr_pkt) {
1455 pr_err("%s: ipc_rtr_pkt alloc failed\n", __func__);
1456 kfree(pkt_fragment_q);
1457 kfree(pkt);
1458 return -ENOMEM;
1459 }
1460
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001461 data = skb_put(ipc_rtr_pkt, sizeof(*msg));
1462 memcpy(data, msg, sizeof(*msg));
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001463 hdr = &(pkt->hdr);
1464 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001465 hdr->type = msg->cmd;
1466 hdr->src_node_id = IPC_ROUTER_NID_LOCAL;
1467 hdr->src_port_id = IPC_ROUTER_ADDRESS;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001468 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001469 hdr->size = sizeof(*msg);
1470 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
1471 hdr->dst_port_id = IPC_ROUTER_ADDRESS;
1472 skb_queue_tail(pkt_fragment_q, ipc_rtr_pkt);
1473 pkt->pkt_fragment_q = pkt_fragment_q;
1474 pkt->length = pkt_size;
1475
1476 ret = post_control_ports(pkt);
1477 release_pkt(pkt);
1478 return ret;
1479}
1480
1481static int broadcast_ctl_msg(union rr_control_msg *ctl)
1482{
1483 struct msm_ipc_router_xprt_info *xprt_info;
1484
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001485 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001486 list_for_each_entry(xprt_info, &xprt_info_list, list) {
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301487 msm_ipc_router_send_control_msg(xprt_info, ctl,
1488 IPC_ROUTER_DUMMY_DEST_NODE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001489 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001490 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001491
1492 return 0;
1493}
1494
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001495static int relay_ctl_msg(struct msm_ipc_router_xprt_info *xprt_info,
1496 union rr_control_msg *ctl)
1497{
1498 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1499
1500 if (!xprt_info || !ctl)
1501 return -EINVAL;
1502
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001503 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001504 list_for_each_entry(fwd_xprt_info, &xprt_info_list, list) {
1505 if (xprt_info->xprt->link_id != fwd_xprt_info->xprt->link_id)
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301506 msm_ipc_router_send_control_msg(fwd_xprt_info, ctl,
1507 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001508 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001509 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001510
1511 return 0;
1512}
1513
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001514static int forward_msg(struct msm_ipc_router_xprt_info *xprt_info,
1515 struct rr_packet *pkt)
1516{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001517 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001518 struct msm_ipc_router_xprt_info *fwd_xprt_info;
1519 struct msm_ipc_routing_table_entry *rt_entry;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001520 int ret = 0;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001521 int fwd_xprt_option;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001522
1523 if (!xprt_info || !pkt)
1524 return -EINVAL;
1525
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001526 hdr = &(pkt->hdr);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001527 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001528 rt_entry = lookup_routing_table(hdr->dst_node_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001529 if (!(rt_entry) || !(rt_entry->xprt_info)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001530 pr_err("%s: Routing table not initialized\n", __func__);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001531 ret = -ENODEV;
1532 goto fm_error1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001533 }
1534
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001535 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001536 fwd_xprt_info = rt_entry->xprt_info;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001537 ret = prepend_header(pkt, fwd_xprt_info);
1538 if (ret < 0) {
1539 pr_err("%s: Prepend Header failed\n", __func__);
1540 goto fm_error2;
1541 }
1542 fwd_xprt_option = fwd_xprt_info->xprt->get_option(fwd_xprt_info->xprt);
1543 if (!(fwd_xprt_option & FRAG_PKT_WRITE_ENABLE)) {
1544 ret = defragment_pkt(pkt);
1545 if (ret < 0)
1546 goto fm_error2;
1547 }
1548
1549 mutex_lock(&fwd_xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001550 if (xprt_info->remote_node_id == fwd_xprt_info->remote_node_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001551 pr_err("%s: Discarding Command to route back\n", __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001552 ret = -EINVAL;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001553 goto fm_error3;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001554 }
1555
1556 if (xprt_info->xprt->link_id == fwd_xprt_info->xprt->link_id) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001557 pr_err("%s: DST in the same cluster\n", __func__);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001558 ret = 0;
1559 goto fm_error3;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001560 }
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07001561 fwd_xprt_info->xprt->write(pkt, pkt->length, fwd_xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001562
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001563fm_error3:
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001564 mutex_unlock(&fwd_xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001565fm_error2:
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001566 up_read(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001567fm_error1:
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001568 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001569
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001570 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001571}
1572
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001573static int msm_ipc_router_send_remove_client(struct comm_mode_info *mode_info,
1574 uint32_t node_id, uint32_t port_id)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001575{
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001576 union rr_control_msg msg;
1577 struct msm_ipc_router_xprt_info *tmp_xprt_info;
1578 int mode;
1579 void *xprt_info;
1580 int rc = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001581
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001582 if (!mode_info) {
1583 pr_err("%s: NULL mode_info\n", __func__);
1584 return -EINVAL;
1585 }
1586 mode = mode_info->mode;
1587 xprt_info = mode_info->xprt_info;
1588
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06001589 memset(&msg, 0, sizeof(msg));
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001590 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1591 msg.cli.node_id = node_id;
1592 msg.cli.port_id = port_id;
1593
1594 if ((mode == SINGLE_LINK_MODE) && xprt_info) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001595 down_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001596 list_for_each_entry(tmp_xprt_info, &xprt_info_list, list) {
1597 if (tmp_xprt_info != xprt_info)
1598 continue;
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301599 msm_ipc_router_send_control_msg(tmp_xprt_info, &msg,
1600 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001601 break;
1602 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001603 up_read(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001604 } else if ((mode == SINGLE_LINK_MODE) && !xprt_info) {
1605 broadcast_ctl_msg_locally(&msg);
1606 } else if (mode == MULTI_LINK_MODE) {
1607 broadcast_ctl_msg(&msg);
1608 broadcast_ctl_msg_locally(&msg);
1609 } else if (mode != NULL_MODE) {
1610 pr_err("%s: Invalid mode(%d) + xprt_inf(%p) for %08x:%08x\n",
1611 __func__, mode, xprt_info, node_id, port_id);
1612 rc = -EINVAL;
1613 }
1614 return rc;
1615}
1616
1617static void update_comm_mode_info(struct comm_mode_info *mode_info,
1618 struct msm_ipc_router_xprt_info *xprt_info)
1619{
1620 if (!mode_info) {
1621 pr_err("%s: NULL mode_info\n", __func__);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001622 return;
1623 }
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06001624
1625 if (mode_info->mode == NULL_MODE) {
1626 mode_info->xprt_info = xprt_info;
1627 mode_info->mode = SINGLE_LINK_MODE;
1628 } else if (mode_info->mode == SINGLE_LINK_MODE &&
1629 mode_info->xprt_info != xprt_info) {
1630 mode_info->mode = MULTI_LINK_MODE;
1631 }
1632
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001633 return;
1634}
1635
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001636static void cleanup_rmt_server(struct msm_ipc_router_xprt_info *xprt_info,
1637 struct msm_ipc_router_remote_port *rport_ptr)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001638{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001639 union rr_control_msg ctl;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001640 struct msm_ipc_server *server = rport_ptr->server;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001641
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001642 D("Remove server %08x:%08x - %08x:%08x",
1643 server->name.service, server->name.instance,
1644 rport_ptr->node_id, rport_ptr->port_id);
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06001645 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001646 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001647 ctl.srv.service = server->name.service;
1648 ctl.srv.instance = server->name.instance;
1649 ctl.srv.node_id = rport_ptr->node_id;
1650 ctl.srv.port_id = rport_ptr->port_id;
1651 relay_ctl_msg(xprt_info, &ctl);
1652 broadcast_ctl_msg_locally(&ctl);
1653 msm_ipc_router_destroy_server(server,
1654 rport_ptr->node_id, rport_ptr->port_id);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001655}
1656
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001657static void cleanup_rmt_ports(struct msm_ipc_router_xprt_info *xprt_info,
1658 struct msm_ipc_routing_table_entry *rt_entry)
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001659{
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001660 struct msm_ipc_router_remote_port *rport_ptr, *tmp_rport_ptr;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001661 union rr_control_msg ctl;
1662 int j;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001663
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06001664 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001665 for (j = 0; j < RP_HASH_SIZE; j++) {
1666 list_for_each_entry_safe(rport_ptr, tmp_rport_ptr,
1667 &rt_entry->remote_port_list[j], list) {
1668 list_del(&rport_ptr->list);
1669 mutex_lock(&rport_ptr->quota_lock_lhb2);
1670 msm_ipc_router_free_resume_tx_port(rport_ptr);
1671 mutex_unlock(&rport_ptr->quota_lock_lhb2);
1672
1673 if (rport_ptr->server)
1674 cleanup_rmt_server(xprt_info, rport_ptr);
1675
1676 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT;
1677 ctl.cli.node_id = rport_ptr->node_id;
1678 ctl.cli.port_id = rport_ptr->port_id;
1679 relay_ctl_msg(xprt_info, &ctl);
1680 broadcast_ctl_msg_locally(&ctl);
1681 kfree(rport_ptr);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001682 }
1683 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001684}
1685
1686static void msm_ipc_cleanup_routing_table(
1687 struct msm_ipc_router_xprt_info *xprt_info)
1688{
1689 int i;
Karthikeyan Ramasubramanian97eec852013-08-06 18:15:52 -06001690 struct msm_ipc_routing_table_entry *rt_entry, *tmp_rt_entry;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001691
1692 if (!xprt_info) {
1693 pr_err("%s: Invalid xprt_info\n", __func__);
1694 return;
1695 }
1696
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001697 down_write(&server_list_lock_lha2);
1698 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001699 for (i = 0; i < RT_HASH_SIZE; i++) {
Karthikeyan Ramasubramanian97eec852013-08-06 18:15:52 -06001700 list_for_each_entry_safe(rt_entry, tmp_rt_entry,
1701 &routing_table[i], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001702 down_write(&rt_entry->lock_lha4);
1703 if (rt_entry->xprt_info != xprt_info) {
1704 up_write(&rt_entry->lock_lha4);
1705 continue;
1706 }
1707 cleanup_rmt_ports(xprt_info, rt_entry);
1708 rt_entry->xprt_info = NULL;
1709 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian97eec852013-08-06 18:15:52 -06001710 list_del(&rt_entry->list);
1711 kfree(rt_entry);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001712 }
1713 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001714 up_write(&routing_table_lock_lha3);
1715 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06001716}
1717
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001718/**
1719 * sync_sec_rule() - Synchrnoize the security rule into the server structure
1720 * @server: Server structure where the rule has to be synchronized.
1721 * @rule: Security tule to be synchronized.
1722 *
1723 * This function is used to update the server structure with the security
1724 * rule configured for the <service:instance> corresponding to that server.
1725 */
1726static void sync_sec_rule(struct msm_ipc_server *server, void *rule)
1727{
1728 struct msm_ipc_server_port *server_port;
1729 struct msm_ipc_router_remote_port *rport_ptr = NULL;
1730
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001731 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001732 list_for_each_entry(server_port, &server->server_port_list, list) {
1733 rport_ptr = msm_ipc_router_lookup_remote_port(
1734 server_port->server_addr.node_id,
1735 server_port->server_addr.port_id);
1736 if (!rport_ptr)
1737 continue;
1738 rport_ptr->sec_rule = rule;
1739 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001740 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001741 server->synced_sec_rule = 1;
1742}
1743
1744/**
1745 * msm_ipc_sync_sec_rule() - Sync the security rule to the service
1746 * @service: Service for which the rule has to be synchronized.
1747 * @instance: Instance for which the rule has to be synchronized.
1748 * @rule: Security rule to be synchronized.
1749 *
1750 * This function is used to syncrhonize the security rule with the server
1751 * hash table, if the user-space script configures the rule after the service
1752 * has come up. This function is used to synchronize the security rule to a
1753 * specific service and optionally a specific instance.
1754 */
1755void msm_ipc_sync_sec_rule(uint32_t service, uint32_t instance, void *rule)
1756{
1757 int key = (service & (SRV_HASH_SIZE - 1));
1758 struct msm_ipc_server *server;
1759
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001760 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001761 list_for_each_entry(server, &server_list[key], list) {
1762 if (server->name.service != service)
1763 continue;
1764
1765 if (server->name.instance != instance &&
1766 instance != ALL_INSTANCE)
1767 continue;
1768
1769 /*
1770 * If the rule applies to all instances and if the specific
1771 * instance of a service has a rule synchronized already,
1772 * do not apply the rule for that specific instance.
1773 */
1774 if (instance == ALL_INSTANCE && server->synced_sec_rule)
1775 continue;
1776
1777 sync_sec_rule(server, rule);
1778 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001779 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001780}
1781
1782/**
1783 * msm_ipc_sync_default_sec_rule() - Default security rule to all services
1784 * @rule: Security rule to be synchronized.
1785 *
1786 * This function is used to syncrhonize the security rule with the server
1787 * hash table, if the user-space script configures the rule after the service
1788 * has come up. This function is used to synchronize the security rule that
1789 * applies to all services, if the concerned service do not have any rule
1790 * defined.
1791 */
1792void msm_ipc_sync_default_sec_rule(void *rule)
1793{
1794 int key;
1795 struct msm_ipc_server *server;
1796
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001797 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001798 for (key = 0; key < SRV_HASH_SIZE; key++) {
1799 list_for_each_entry(server, &server_list[key], list) {
1800 if (server->synced_sec_rule)
1801 continue;
1802
1803 sync_sec_rule(server, rule);
1804 }
1805 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001806 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001807}
1808
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001809static int process_hello_msg(struct msm_ipc_router_xprt_info *xprt_info,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001810 struct rr_header_v1 *hdr)
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001811{
1812 int i, rc = 0;
1813 union rr_control_msg ctl;
1814 struct msm_ipc_routing_table_entry *rt_entry;
1815
1816 if (!hdr)
1817 return -EINVAL;
1818
1819 RR("o HELLO NID %d\n", hdr->src_node_id);
1820
1821 xprt_info->remote_node_id = hdr->src_node_id;
1822 /*
1823 * Find the entry from Routing Table corresponding to Node ID.
1824 * Under SSR, an entry will be found. When the system boots up
1825 * for the 1st time, an entry will not be found and hence allocate
1826 * an entry. Update the entry with the Node ID that it corresponds
1827 * to and the XPRT through which it can be reached.
1828 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001829 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001830 rt_entry = lookup_routing_table(hdr->src_node_id);
1831 if (!rt_entry) {
1832 rt_entry = alloc_routing_table_entry(hdr->src_node_id);
1833 if (!rt_entry) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001834 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001835 pr_err("%s: rt_entry allocation failed\n", __func__);
1836 return -ENOMEM;
1837 }
1838 add_routing_table_entry(rt_entry);
1839 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001840 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001841 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1842 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001843 up_write(&rt_entry->lock_lha4);
1844 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001845
1846 /* Send a reply HELLO message */
1847 memset(&ctl, 0, sizeof(ctl));
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001848 ctl.hello.cmd = IPC_ROUTER_CTRL_CMD_HELLO;
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05301849 rc = msm_ipc_router_send_control_msg(xprt_info, &ctl,
1850 IPC_ROUTER_DUMMY_DEST_NODE);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001851 if (rc < 0) {
1852 pr_err("%s: Error sending reply HELLO message\n", __func__);
1853 return rc;
1854 }
1855 xprt_info->initialized = 1;
1856
1857 /*
1858 * Send list of servers from the local node and from nodes
1859 * outside the mesh network in which this XPRT is part of.
1860 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001861 down_read(&server_list_lock_lha2);
1862 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001863 for (i = 0; i < RT_HASH_SIZE; i++) {
1864 list_for_each_entry(rt_entry, &routing_table[i], list) {
1865 if ((rt_entry->node_id != IPC_ROUTER_NID_LOCAL) &&
Karthikeyan Ramasubramanian72ad5792013-01-30 14:17:57 -07001866 (!rt_entry->xprt_info ||
1867 (rt_entry->xprt_info->xprt->link_id ==
1868 xprt_info->xprt->link_id)))
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001869 continue;
1870 rc = msm_ipc_router_send_server_list(rt_entry->node_id,
1871 xprt_info);
1872 if (rc < 0) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001873 up_read(&routing_table_lock_lha3);
1874 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001875 return rc;
1876 }
1877 }
1878 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001879 up_read(&routing_table_lock_lha3);
1880 up_read(&server_list_lock_lha2);
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06001881 RR("HELLO message processed\n");
1882 return rc;
1883}
1884
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001885static int process_resume_tx_msg(union rr_control_msg *msg,
1886 struct rr_packet *pkt)
1887{
1888 struct msm_ipc_router_remote_port *rport_ptr;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001889 int ret = 0;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001890
1891 RR("o RESUME_TX id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
1892
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001893 down_read(&local_ports_lock_lha2);
1894 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001895 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
1896 msg->cli.port_id);
1897 if (!rport_ptr) {
1898 pr_err("%s: Unable to resume client\n", __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001899 ret = -ENODEV;
1900 goto prtm_out;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001901 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001902 mutex_lock(&rport_ptr->quota_lock_lhb2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001903 rport_ptr->tx_quota_cnt = 0;
1904 post_resume_tx(rport_ptr, pkt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001905 mutex_unlock(&rport_ptr->quota_lock_lhb2);
1906prtm_out:
1907 up_read(&routing_table_lock_lha3);
1908 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001909 return 0;
1910}
1911
1912static int process_new_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
1913 union rr_control_msg *msg, struct rr_packet *pkt)
1914{
1915 struct msm_ipc_routing_table_entry *rt_entry;
1916 struct msm_ipc_server *server;
1917 struct msm_ipc_router_remote_port *rport_ptr;
1918
1919 if (msg->srv.instance == 0) {
1920 pr_err("%s: Server %08x create rejected, version = 0\n",
1921 __func__, msg->srv.service);
1922 return -EINVAL;
1923 }
1924
1925 RR("o NEW_SERVER id=%d:%08x service=%08x:%08x\n", msg->srv.node_id,
1926 msg->srv.port_id, msg->srv.service, msg->srv.instance);
1927 /*
1928 * Find the entry from Routing Table corresponding to Node ID.
1929 * Under SSR, an entry will be found. When the subsystem hosting
1930 * service is not adjacent, an entry will not be found and hence
1931 * allocate an entry. Update the entry with the Node ID that it
1932 * corresponds to and the XPRT through which it can be reached.
1933 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001934 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001935 rt_entry = lookup_routing_table(msg->srv.node_id);
1936 if (!rt_entry) {
1937 rt_entry = alloc_routing_table_entry(msg->srv.node_id);
1938 if (!rt_entry) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001939 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001940 pr_err("%s: rt_entry allocation failed\n", __func__);
1941 return -ENOMEM;
1942 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001943 down_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001944 rt_entry->neighbor_node_id = xprt_info->remote_node_id;
1945 rt_entry->xprt_info = xprt_info;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001946 up_write(&rt_entry->lock_lha4);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001947 add_routing_table_entry(rt_entry);
1948 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001949 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001950
1951 /*
1952 * If the service does not exist already in the database, create and
1953 * store the service info. Create a remote port structure in which
1954 * the service is hosted and cache the security rule for the service
1955 * in that remote port structure.
1956 */
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001957 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001958 server = msm_ipc_router_lookup_server(msg->srv.service,
1959 msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
1960 if (!server) {
1961 server = msm_ipc_router_create_server(
1962 msg->srv.service, msg->srv.instance,
1963 msg->srv.node_id, msg->srv.port_id, xprt_info);
1964 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001965 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001966 pr_err("%s: Server Create failed\n", __func__);
1967 return -ENOMEM;
1968 }
1969
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001970 down_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001971 if (!msm_ipc_router_lookup_remote_port(
1972 msg->srv.node_id, msg->srv.port_id)) {
1973 rport_ptr = msm_ipc_router_create_remote_port(
1974 msg->srv.node_id, msg->srv.port_id);
1975 if (!rport_ptr) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001976 up_read(&routing_table_lock_lha3);
1977 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001978 return -ENOMEM;
1979 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001980 rport_ptr->server = server;
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001981 rport_ptr->sec_rule = msm_ipc_get_security_rule(
1982 msg->srv.service,
1983 msg->srv.instance);
1984 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001985 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001986 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06001987 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001988
1989 /*
1990 * Relay the new server message to other subsystems that do not belong
1991 * to the cluster from which this message is received. Notify the
1992 * local clients waiting for this service.
1993 */
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06001994 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06001995 post_control_ports(pkt);
1996 return 0;
1997}
1998
1999static int process_rmv_server_msg(struct msm_ipc_router_xprt_info *xprt_info,
2000 union rr_control_msg *msg, struct rr_packet *pkt)
2001{
2002 struct msm_ipc_server *server;
2003
2004 RR("o REMOVE_SERVER service=%08x:%d\n",
2005 msg->srv.service, msg->srv.instance);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002006 down_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002007 server = msm_ipc_router_lookup_server(msg->srv.service,
2008 msg->srv.instance, msg->srv.node_id, msg->srv.port_id);
2009 if (server) {
2010 msm_ipc_router_destroy_server(server, msg->srv.node_id,
2011 msg->srv.port_id);
2012 /*
2013 * Relay the new server message to other subsystems that do not
2014 * belong to the cluster from which this message is received.
2015 * Notify the local clients communicating with the service.
2016 */
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002017 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002018 post_control_ports(pkt);
2019 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002020 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002021 return 0;
2022}
2023
2024static int process_rmv_client_msg(struct msm_ipc_router_xprt_info *xprt_info,
2025 union rr_control_msg *msg, struct rr_packet *pkt)
2026{
2027 struct msm_ipc_router_remote_port *rport_ptr;
2028
2029 RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.node_id, msg->cli.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002030 down_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002031 rport_ptr = msm_ipc_router_lookup_remote_port(msg->cli.node_id,
2032 msg->cli.port_id);
2033 if (rport_ptr)
2034 msm_ipc_router_destroy_remote_port(rport_ptr);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002035 up_write(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002036
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002037 relay_ctl_msg(xprt_info, msg);
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002038 post_control_ports(pkt);
2039 return 0;
2040}
2041
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002042static int process_control_msg(struct msm_ipc_router_xprt_info *xprt_info,
2043 struct rr_packet *pkt)
2044{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002045 union rr_control_msg *msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002046 int rc = 0;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002047 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002048
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002049 if (pkt->length != sizeof(*msg)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002050 pr_err("%s: r2r msg size %d != %d\n", __func__, pkt->length,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002051 sizeof(*msg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002052 return -EINVAL;
2053 }
2054
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002055 hdr = &(pkt->hdr);
2056 msg = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, sizeof(*msg));
2057 if (!msg) {
2058 pr_err("%s: Error extracting control msg\n", __func__);
2059 return -ENOMEM;
Karthikeyan Ramasubramanian9024dd82011-12-19 18:44:19 -07002060 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002061
2062 switch (msg->cmd) {
2063 case IPC_ROUTER_CTRL_CMD_HELLO:
Karthikeyan Ramasubramanianb234c242012-10-23 13:12:44 -06002064 rc = process_hello_msg(xprt_info, hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002065 break;
2066 case IPC_ROUTER_CTRL_CMD_RESUME_TX:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002067 rc = process_resume_tx_msg(msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002068 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002069 case IPC_ROUTER_CTRL_CMD_NEW_SERVER:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002070 rc = process_new_server_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002071 break;
2072 case IPC_ROUTER_CTRL_CMD_REMOVE_SERVER:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002073 rc = process_rmv_server_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002074 break;
2075 case IPC_ROUTER_CTRL_CMD_REMOVE_CLIENT:
Karthikeyan Ramasubramanian4b4120e2013-05-17 18:18:20 -06002076 rc = process_rmv_client_msg(xprt_info, msg, pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002077 break;
2078 case IPC_ROUTER_CTRL_CMD_PING:
2079 /* No action needed for ping messages received */
2080 RR("o PING\n");
2081 break;
2082 default:
2083 RR("o UNKNOWN(%08x)\n", msg->cmd);
2084 rc = -ENOSYS;
2085 }
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002086 kfree(msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002087 return rc;
2088}
2089
2090static void do_read_data(struct work_struct *work)
2091{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002092 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002093 struct rr_packet *pkt = NULL;
2094 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002095 struct msm_ipc_router_remote_port *rport_ptr;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002096 int ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002097
2098 struct msm_ipc_router_xprt_info *xprt_info =
2099 container_of(work,
2100 struct msm_ipc_router_xprt_info,
2101 read_data);
2102
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002103 while ((pkt = rr_read(xprt_info)) != NULL) {
Karthikeyan Ramasubramanian94337602013-09-17 19:31:59 -06002104 if (pkt->length < calc_rx_header_size(xprt_info) ||
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002105 pkt->length > MAX_IPC_PKT_SIZE) {
2106 pr_err("%s: Invalid pkt length %d\n",
2107 __func__, pkt->length);
2108 goto fail_data;
2109 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002110
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002111 ret = extract_header(pkt);
2112 if (ret < 0)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002113 goto fail_data;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002114 hdr = &(pkt->hdr);
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06002115 RAW("ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n",
2116 hdr->version, hdr->type, hdr->src_node_id,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002117 hdr->src_port_id, hdr->control_flag, hdr->size,
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06002118 hdr->dst_node_id, hdr->dst_port_id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002119
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002120 if ((hdr->dst_node_id != IPC_ROUTER_NID_LOCAL) &&
2121 ((hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX) ||
2122 (hdr->type == IPC_ROUTER_CTRL_CMD_DATA))) {
2123 forward_msg(xprt_info, pkt);
2124 release_pkt(pkt);
2125 continue;
2126 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002127
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002128 if (hdr->type != IPC_ROUTER_CTRL_CMD_DATA) {
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002129 process_control_msg(xprt_info, pkt);
2130 release_pkt(pkt);
2131 continue;
2132 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002133#if defined(CONFIG_MSM_SMD_LOGGING)
2134#if defined(DEBUG)
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002135 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2136 smem_log_event((SMEM_LOG_PROC_ID_APPS |
Zaheerulla Meer1ee914e2013-06-19 16:31:17 +05302137 SMEM_LOG_IPC_ROUTER_EVENT_BASE |
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002138 IPC_ROUTER_LOG_EVENT_RX),
2139 (hdr->src_node_id << 24) |
2140 (hdr->src_port_id & 0xffffff),
2141 (hdr->dst_node_id << 24) |
2142 (hdr->dst_port_id & 0xffffff),
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002143 (hdr->type << 24) | (hdr->control_flag << 16) |
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002144 (hdr->size & 0xffff));
2145 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002146#endif
2147#endif
2148
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002149 down_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002150 port_ptr = msm_ipc_router_lookup_local_port(hdr->dst_port_id);
2151 if (!port_ptr) {
2152 pr_err("%s: No local port id %08x\n", __func__,
2153 hdr->dst_port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002154 up_read(&local_ports_lock_lha2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002155 release_pkt(pkt);
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302156 return;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002157 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002158
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002159 down_read(&routing_table_lock_lha3);
2160 rport_ptr = msm_ipc_router_lookup_remote_port(hdr->src_node_id,
2161 hdr->src_port_id);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002162 if (!rport_ptr) {
2163 rport_ptr = msm_ipc_router_create_remote_port(
2164 hdr->src_node_id,
2165 hdr->src_port_id);
2166 if (!rport_ptr) {
2167 pr_err("%s: Rmt Prt %08x:%08x create failed\n",
2168 __func__, hdr->src_node_id,
2169 hdr->src_port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002170 up_read(&routing_table_lock_lha3);
2171 up_read(&local_ports_lock_lha2);
Zaheerulla Meer6a309de2013-07-12 16:16:30 +05302172 release_pkt(pkt);
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302173 return;
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06002174 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002175 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002176 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06002177 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002178 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002179 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002180 return;
2181
2182fail_data:
2183 release_pkt(pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002184 pr_err("ipc_router has died\n");
2185}
2186
2187int msm_ipc_router_register_server(struct msm_ipc_port *port_ptr,
2188 struct msm_ipc_addr *name)
2189{
2190 struct msm_ipc_server *server;
2191 unsigned long flags;
2192 union rr_control_msg ctl;
2193
2194 if (!port_ptr || !name)
2195 return -EINVAL;
2196
2197 if (name->addrtype != MSM_IPC_ADDR_NAME)
2198 return -EINVAL;
2199
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002200 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002201 server = msm_ipc_router_lookup_server(name->addr.port_name.service,
2202 name->addr.port_name.instance,
2203 IPC_ROUTER_NID_LOCAL,
2204 port_ptr->this_port.port_id);
2205 if (server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002206 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002207 pr_err("%s: Server already present\n", __func__);
2208 return -EINVAL;
2209 }
2210
2211 server = msm_ipc_router_create_server(name->addr.port_name.service,
2212 name->addr.port_name.instance,
2213 IPC_ROUTER_NID_LOCAL,
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06002214 port_ptr->this_port.port_id,
2215 NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002216 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002217 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002218 pr_err("%s: Server Creation failed\n", __func__);
2219 return -EINVAL;
2220 }
2221
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06002222 memset(&ctl, 0, sizeof(ctl));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002223 ctl.cmd = IPC_ROUTER_CTRL_CMD_NEW_SERVER;
2224 ctl.srv.service = server->name.service;
2225 ctl.srv.instance = server->name.instance;
2226 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
2227 ctl.srv.port_id = port_ptr->this_port.port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002228 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002229 broadcast_ctl_msg(&ctl);
Karthikeyan Ramasubramanianfa807952013-08-06 18:04:12 -06002230 broadcast_ctl_msg_locally(&ctl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002231 spin_lock_irqsave(&port_ptr->port_lock, flags);
2232 port_ptr->type = SERVER_PORT;
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002233 port_ptr->mode_info.mode = MULTI_LINK_MODE;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002234 port_ptr->port_name.service = server->name.service;
2235 port_ptr->port_name.instance = server->name.instance;
2236 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
2237 return 0;
2238}
2239
2240int msm_ipc_router_unregister_server(struct msm_ipc_port *port_ptr)
2241{
2242 struct msm_ipc_server *server;
2243 unsigned long flags;
2244 union rr_control_msg ctl;
2245
2246 if (!port_ptr)
2247 return -EINVAL;
2248
2249 if (port_ptr->type != SERVER_PORT) {
2250 pr_err("%s: Trying to unregister a non-server port\n",
2251 __func__);
2252 return -EINVAL;
2253 }
2254
2255 if (port_ptr->this_port.node_id != IPC_ROUTER_NID_LOCAL) {
2256 pr_err("%s: Trying to unregister a remote server locally\n",
2257 __func__);
2258 return -EINVAL;
2259 }
2260
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002261 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002262 server = msm_ipc_router_lookup_server(port_ptr->port_name.service,
2263 port_ptr->port_name.instance,
2264 port_ptr->this_port.node_id,
2265 port_ptr->this_port.port_id);
2266 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002267 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002268 pr_err("%s: Server lookup failed\n", __func__);
2269 return -ENODEV;
2270 }
2271
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06002272 memset(&ctl, 0, sizeof(ctl));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002273 ctl.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2274 ctl.srv.service = server->name.service;
2275 ctl.srv.instance = server->name.instance;
2276 ctl.srv.node_id = IPC_ROUTER_NID_LOCAL;
2277 ctl.srv.port_id = port_ptr->this_port.port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002278 msm_ipc_router_destroy_server(server, port_ptr->this_port.node_id,
2279 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002280 up_write(&server_list_lock_lha2);
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002281 broadcast_ctl_msg(&ctl);
Karthikeyan Ramasubramanianfa807952013-08-06 18:04:12 -06002282 broadcast_ctl_msg_locally(&ctl);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002283 spin_lock_irqsave(&port_ptr->port_lock, flags);
2284 port_ptr->type = CLIENT_PORT;
2285 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
2286 return 0;
2287}
2288
2289static int loopback_data(struct msm_ipc_port *src,
2290 uint32_t port_id,
2291 struct sk_buff_head *data)
2292{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002293 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002294 struct msm_ipc_port *port_ptr;
2295 struct rr_packet *pkt;
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002296 int ret_len;
Karthikeyan Ramasubramanian50765642013-09-04 23:34:38 -06002297 struct sk_buff *temp_skb;
2298 int align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002299
2300 if (!data) {
2301 pr_err("%s: Invalid pkt pointer\n", __func__);
2302 return -EINVAL;
2303 }
2304
2305 pkt = create_pkt(data);
2306 if (!pkt) {
2307 pr_err("%s: New pkt create failed\n", __func__);
2308 return -ENOMEM;
2309 }
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002310 hdr = &(pkt->hdr);
2311 hdr->version = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002312 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2313 hdr->src_node_id = src->this_port.node_id;
2314 hdr->src_port_id = src->this_port.port_id;
2315 hdr->size = pkt->length;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002316 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002317 hdr->dst_node_id = IPC_ROUTER_NID_LOCAL;
2318 hdr->dst_port_id = port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002319
Karthikeyan Ramasubramanian50765642013-09-04 23:34:38 -06002320 temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
2321 align_size = ALIGN_SIZE(pkt->length);
2322 skb_put(temp_skb, align_size);
2323 pkt->length += align_size;
2324
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002325 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002326 port_ptr = msm_ipc_router_lookup_local_port(port_id);
2327 if (!port_ptr) {
2328 pr_err("%s: Local port %d not present\n", __func__, port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002329 up_read(&local_ports_lock_lha2);
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +05302330 pkt->pkt_fragment_q = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002331 release_pkt(pkt);
2332 return -ENODEV;
2333 }
2334
Karthikeyan Ramasubramanian50765642013-09-04 23:34:38 -06002335 ret_len = hdr->size;
Karthikeyan Ramasubramaniand1af6252013-05-17 15:19:56 -06002336 post_pkt_to_port(port_ptr, pkt, 0);
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002337 update_comm_mode_info(&src->mode_info, NULL);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002338 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002339
Karthikeyan Ramasubramanian0a801c12013-02-08 13:07:42 -07002340 return ret_len;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002341}
2342
2343static int msm_ipc_router_write_pkt(struct msm_ipc_port *src,
2344 struct msm_ipc_router_remote_port *rport_ptr,
2345 struct rr_packet *pkt)
2346{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002347 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002348 struct msm_ipc_router_xprt_info *xprt_info;
2349 struct msm_ipc_routing_table_entry *rt_entry;
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302350 struct msm_ipc_resume_tx_port *resume_tx_port;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002351 struct sk_buff *temp_skb;
2352 int xprt_option;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002353 int ret;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002354 int align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002355
2356 if (!rport_ptr || !src || !pkt)
2357 return -EINVAL;
2358
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002359 hdr = &(pkt->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002360 hdr->type = IPC_ROUTER_CTRL_CMD_DATA;
2361 hdr->src_node_id = src->this_port.node_id;
2362 hdr->src_port_id = src->this_port.port_id;
2363 hdr->size = pkt->length;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002364 hdr->control_flag = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002365 hdr->dst_node_id = rport_ptr->node_id;
2366 hdr->dst_port_id = rport_ptr->port_id;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002367
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002368 mutex_lock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302369 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA) {
2370 if (msm_ipc_router_lookup_resume_tx_port(
2371 rport_ptr, src->this_port.port_id)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002372 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302373 return -EAGAIN;
2374 }
2375 resume_tx_port =
2376 kzalloc(sizeof(struct msm_ipc_resume_tx_port),
2377 GFP_KERNEL);
2378 if (!resume_tx_port) {
2379 pr_err("%s: Resume_Tx port allocation failed\n",
2380 __func__);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002381 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302382 return -ENOMEM;
2383 }
2384 INIT_LIST_HEAD(&resume_tx_port->list);
2385 resume_tx_port->port_id = src->this_port.port_id;
2386 resume_tx_port->node_id = src->this_port.node_id;
2387 list_add_tail(&resume_tx_port->list,
2388 &rport_ptr->resume_tx_port_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002389 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Zaheerulla Meera34fc662013-04-17 01:16:47 +05302390 return -EAGAIN;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002391 }
2392 rport_ptr->tx_quota_cnt++;
2393 if (rport_ptr->tx_quota_cnt == IPC_ROUTER_DEFAULT_RX_QUOTA)
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002394 hdr->control_flag |= CONTROL_FLAG_CONFIRM_RX;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002395 mutex_unlock(&rport_ptr->quota_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002396
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002397 rt_entry = lookup_routing_table(hdr->dst_node_id);
2398 if (!rt_entry || !rt_entry->xprt_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002399 pr_err("%s: Remote node %d not up\n",
2400 __func__, hdr->dst_node_id);
2401 return -ENODEV;
2402 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002403 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002404 xprt_info = rt_entry->xprt_info;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002405 ret = prepend_header(pkt, xprt_info);
2406 if (ret < 0) {
2407 up_read(&rt_entry->lock_lha4);
2408 pr_err("%s: Prepend Header failed\n", __func__);
2409 return ret;
2410 }
2411 xprt_option = xprt_info->xprt->get_option(xprt_info->xprt);
2412 if (!(xprt_option & FRAG_PKT_WRITE_ENABLE)) {
2413 ret = defragment_pkt(pkt);
2414 if (ret < 0) {
2415 up_read(&rt_entry->lock_lha4);
2416 return ret;
2417 }
2418 }
2419
2420 temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
2421 align_size = ALIGN_SIZE(pkt->length);
2422 skb_put(temp_skb, align_size);
2423 pkt->length += align_size;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002424 mutex_lock(&xprt_info->tx_lock_lhb2);
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002425 ret = xprt_info->xprt->write(pkt, pkt->length, xprt_info->xprt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002426 mutex_unlock(&xprt_info->tx_lock_lhb2);
2427 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002428
2429 if (ret < 0) {
2430 pr_err("%s: Write on XPRT failed\n", __func__);
2431 return ret;
2432 }
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002433 update_comm_mode_info(&src->mode_info, xprt_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002434
2435 RAW_HDR("[w rr_h] "
2436 "ver=%i,type=%s,src_nid=%08x,src_port_id=%08x,"
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002437 "control_flag=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002438 hdr->version, type_to_str(hdr->type),
2439 hdr->src_node_id, hdr->src_port_id,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002440 hdr->control_flag, hdr->size,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002441 hdr->dst_node_id, hdr->dst_port_id);
2442
2443#if defined(CONFIG_MSM_SMD_LOGGING)
2444#if defined(DEBUG)
2445 if (msm_ipc_router_debug_mask & SMEM_LOG) {
2446 smem_log_event((SMEM_LOG_PROC_ID_APPS |
Zaheerulla Meer1ee914e2013-06-19 16:31:17 +05302447 SMEM_LOG_IPC_ROUTER_EVENT_BASE |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002448 IPC_ROUTER_LOG_EVENT_TX),
2449 (hdr->src_node_id << 24) |
2450 (hdr->src_port_id & 0xffffff),
2451 (hdr->dst_node_id << 24) |
2452 (hdr->dst_port_id & 0xffffff),
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002453 (hdr->type << 24) | (hdr->control_flag << 16) |
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002454 (hdr->size & 0xffff));
2455 }
2456#endif
2457#endif
2458
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002459 return hdr->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002460}
2461
2462int msm_ipc_router_send_to(struct msm_ipc_port *src,
2463 struct sk_buff_head *data,
2464 struct msm_ipc_addr *dest)
2465{
2466 uint32_t dst_node_id = 0, dst_port_id = 0;
2467 struct msm_ipc_server *server;
2468 struct msm_ipc_server_port *server_port;
2469 struct msm_ipc_router_remote_port *rport_ptr = NULL;
2470 struct rr_packet *pkt;
2471 int ret;
2472
2473 if (!src || !data || !dest) {
2474 pr_err("%s: Invalid Parameters\n", __func__);
2475 return -EINVAL;
2476 }
2477
2478 /* Resolve Address*/
2479 if (dest->addrtype == MSM_IPC_ADDR_ID) {
2480 dst_node_id = dest->addr.port_addr.node_id;
2481 dst_port_id = dest->addr.port_addr.port_id;
2482 } else if (dest->addrtype == MSM_IPC_ADDR_NAME) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002483 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002484 server = msm_ipc_router_lookup_server(
2485 dest->addr.port_name.service,
2486 dest->addr.port_name.instance,
2487 0, 0);
2488 if (!server) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002489 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002490 pr_err("%s: Destination not reachable\n", __func__);
2491 return -ENODEV;
2492 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002493 server_port = list_first_entry(&server->server_port_list,
2494 struct msm_ipc_server_port,
2495 list);
2496 dst_node_id = server_port->server_addr.node_id;
2497 dst_port_id = server_port->server_addr.port_id;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002498 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002499 }
2500 if (dst_node_id == IPC_ROUTER_NID_LOCAL) {
2501 ret = loopback_data(src, dst_port_id, data);
2502 return ret;
2503 }
2504
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002505 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002506 rport_ptr = msm_ipc_router_lookup_remote_port(dst_node_id,
2507 dst_port_id);
2508 if (!rport_ptr) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002509 up_read(&routing_table_lock_lha3);
Zaheerulla Meer2c515312013-05-10 15:51:28 +05302510 pr_err("%s: Remote port not found\n", __func__);
2511 return -ENODEV;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002512 }
2513
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002514 if (src->check_send_permissions) {
2515 ret = src->check_send_permissions(rport_ptr->sec_rule);
2516 if (ret <= 0) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002517 up_read(&routing_table_lock_lha3);
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06002518 pr_err("%s: permission failure for %s\n",
2519 __func__, current->comm);
2520 return -EPERM;
2521 }
2522 }
2523
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002524 pkt = create_pkt(data);
2525 if (!pkt) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002526 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002527 pr_err("%s: Pkt creation failed\n", __func__);
2528 return -ENOMEM;
2529 }
2530
2531 ret = msm_ipc_router_write_pkt(src, rport_ptr, pkt);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002532 up_read(&routing_table_lock_lha3);
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +05302533 if (ret < 0)
2534 pkt->pkt_fragment_q = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002535 release_pkt(pkt);
2536
2537 return ret;
2538}
2539
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002540int msm_ipc_router_send_msg(struct msm_ipc_port *src,
2541 struct msm_ipc_addr *dest,
2542 void *data, unsigned int data_len)
2543{
2544 struct sk_buff_head *out_skb_head;
2545 int ret;
2546
2547 out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
2548 if (!out_skb_head) {
2549 pr_err("%s: SKB conversion failed\n", __func__);
2550 return -EFAULT;
2551 }
2552
2553 ret = msm_ipc_router_send_to(src, out_skb_head, dest);
2554 if (ret < 0) {
Zaheerulla Meer7ccb9672013-08-20 12:02:31 +05302555 if (ret != -EAGAIN)
2556 pr_err("%s: msm_ipc_router_send_to failed - ret: %d\n",
2557 __func__, ret);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002558 msm_ipc_router_free_skb(out_skb_head);
Zaheerulla Meerfc59a8a2013-06-26 22:39:00 +05302559 return ret;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002560 }
2561 return 0;
2562}
2563
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302564/**
2565 * msm_ipc_router_send_resume_tx() - Send Resume_Tx message
2566 * @data: Pointer to received data packet that has confirm_rx bit set
2567 *
2568 * @return: On success, number of bytes transferred is returned, else
2569 * standard linux error code is returned.
2570 *
2571 * This function sends the Resume_Tx event to the remote node that
2572 * sent the data with confirm_rx field set. In case of a multi-hop
2573 * scenario also, this function makes sure that the destination node_id
2574 * to which the resume_tx event should reach is right.
2575 */
2576static int msm_ipc_router_send_resume_tx(void *data)
2577{
2578 union rr_control_msg msg;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002579 struct rr_header_v1 *hdr = (struct rr_header_v1 *)data;
Zaheerulla Meer3ce2dfb2013-07-11 19:24:40 +05302580 struct msm_ipc_routing_table_entry *rt_entry;
2581 int ret;
2582
2583 memset(&msg, 0, sizeof(msg));
2584 msg.cmd = IPC_ROUTER_CTRL_CMD_RESUME_TX;
2585 msg.cli.node_id = hdr->dst_node_id;
2586 msg.cli.port_id = hdr->dst_port_id;
2587 down_read(&routing_table_lock_lha3);
2588 rt_entry = lookup_routing_table(hdr->src_node_id);
2589 if (!rt_entry) {
2590 pr_err("%s: %d Node is not present",
2591 __func__, hdr->src_node_id);
2592 up_read(&routing_table_lock_lha3);
2593 return -ENODEV;
2594 }
2595 RR("x RESUME_TX id=%d:%08x\n",
2596 msg.cli.node_id, msg.cli.port_id);
2597 ret = msm_ipc_router_send_control_msg(rt_entry->xprt_info, &msg,
2598 hdr->src_node_id);
2599 up_read(&routing_table_lock_lha3);
2600 if (ret < 0)
2601 pr_err("%s: Send Resume_Tx Failed SRC_NODE: %d SRC_PORT: %d DEST_NODE: %d",
2602 __func__, hdr->dst_node_id, hdr->dst_port_id,
2603 hdr->src_node_id);
2604
2605 return ret;
2606}
2607
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002608int msm_ipc_router_read(struct msm_ipc_port *port_ptr,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002609 struct rr_packet **read_pkt,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002610 size_t buf_len)
2611{
2612 struct rr_packet *pkt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002613
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002614 if (!port_ptr || !read_pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002615 return -EINVAL;
2616
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002617 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002618 if (list_empty(&port_ptr->port_rx_q)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002619 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002620 return -EAGAIN;
2621 }
2622
2623 pkt = list_first_entry(&port_ptr->port_rx_q, struct rr_packet, list);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002624 if ((buf_len) && (pkt->hdr.size > buf_len)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002625 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002626 return -ETOOSMALL;
2627 }
2628 list_del(&pkt->list);
2629 if (list_empty(&port_ptr->port_rx_q))
2630 wake_unlock(&port_ptr->port_rx_wake_lock);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002631 *read_pkt = pkt;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002632 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002633 if (pkt->hdr.control_flag & CONTROL_FLAG_CONFIRM_RX)
2634 msm_ipc_router_send_resume_tx(&pkt->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002635
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002636 return pkt->length;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002637}
2638
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302639/**
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302640 * msm_ipc_router_rx_data_wait() - Wait for new message destined to a local port.
2641 * @port_ptr: Pointer to the local port
2642 * @timeout: < 0 timeout indicates infinite wait till a message arrives.
2643 * > 0 timeout indicates the wait time.
2644 * 0 indicates that we do not wait.
2645 * @return: 0 if there are pending messages to read,
2646 * standard Linux error code otherwise.
2647 *
2648 * Checks for the availability of messages that are destined to a local port.
2649 * If no messages are present then waits as per @timeout.
2650 */
2651int msm_ipc_router_rx_data_wait(struct msm_ipc_port *port_ptr, long timeout)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002652{
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302653 int ret = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002654
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302655 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002656 while (list_empty(&port_ptr->port_rx_q)) {
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302657 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002658 if (timeout < 0) {
2659 ret = wait_event_interruptible(
2660 port_ptr->port_rx_wait_q,
2661 !list_empty(&port_ptr->port_rx_q));
2662 if (ret)
2663 return ret;
2664 } else if (timeout > 0) {
2665 timeout = wait_event_interruptible_timeout(
2666 port_ptr->port_rx_wait_q,
2667 !list_empty(&port_ptr->port_rx_q),
2668 timeout);
2669 if (timeout < 0)
2670 return -EFAULT;
2671 }
2672 if (timeout == 0)
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302673 return -ENOMSG;
2674 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002675 }
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302676 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002677
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302678 return ret;
2679}
2680
2681/**
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302682 * msm_ipc_router_recv_from() - Recieve messages destined to a local port.
2683 * @port_ptr: Pointer to the local port
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002684 * @pkt : Pointer to the router-to-router packet
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302685 * @src: Pointer to local port address
2686 * @timeout: < 0 timeout indicates infinite wait till a message arrives.
2687 * > 0 timeout indicates the wait time.
2688 * 0 indicates that we do not wait.
2689 * @return: = Number of bytes read(On successful read operation).
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302690 * = -ENOMSG (If there are no pending messages and timeout is 0).
Zaheerulla Meer20e84262013-05-22 23:31:59 +05302691 * = -EINVAL (If either of the arguments, port_ptr or data is invalid)
2692 * = -EFAULT (If there are no pending messages when timeout is > 0
2693 * and the wait_event_interruptible_timeout has returned value > 0)
2694 * = -ERESTARTSYS (If there are no pending messages when timeout
2695 * is < 0 and wait_event_interruptible was interrupted by a signal)
2696 *
2697 * This function reads the messages that are destined for a local port. It
2698 * is used by modules that exist with-in the kernel and use IPC Router for
2699 * transport. The function checks if there are any messages that are already
2700 * received. If yes, it reads them, else it waits as per the timeout value.
2701 * On a successful read, the return value of the function indicates the number
2702 * of bytes that are read.
2703 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002704int msm_ipc_router_recv_from(struct msm_ipc_port *port_ptr,
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002705 struct rr_packet **pkt,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002706 struct msm_ipc_addr *src,
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002707 long timeout)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002708{
2709 int ret, data_len, align_size;
2710 struct sk_buff *temp_skb;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002711 struct rr_header_v1 *hdr = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002712
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002713 if (!port_ptr || !pkt) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002714 pr_err("%s: Invalid pointers being passed\n", __func__);
2715 return -EINVAL;
2716 }
2717
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002718 *pkt = NULL;
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302719
2720 ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
2721 if (ret)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002722 return ret;
2723
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002724 ret = msm_ipc_router_read(port_ptr, pkt, 0);
2725 if (ret <= 0 || !(*pkt))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002726 return ret;
2727
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002728 hdr = &((*pkt)->hdr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002729 if (src) {
2730 src->addrtype = MSM_IPC_ADDR_ID;
2731 src->addr.port_addr.node_id = hdr->src_node_id;
2732 src->addr.port_addr.port_id = hdr->src_port_id;
2733 }
2734
2735 data_len = hdr->size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002736 align_size = ALIGN_SIZE(data_len);
2737 if (align_size) {
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002738 temp_skb = skb_peek_tail((*pkt)->pkt_fragment_q);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002739 skb_trim(temp_skb, (temp_skb->len - align_size));
2740 }
2741 return data_len;
2742}
2743
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002744int msm_ipc_router_read_msg(struct msm_ipc_port *port_ptr,
2745 struct msm_ipc_addr *src,
2746 unsigned char **data,
2747 unsigned int *len)
2748{
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002749 struct rr_packet *pkt;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002750 int ret;
2751
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002752 ret = msm_ipc_router_recv_from(port_ptr, &pkt, src, 0);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002753 if (ret < 0) {
Arun Kumar Neelakantam0f73f432013-06-21 17:57:18 +05302754 if (ret != -ENOMSG)
2755 pr_err("%s: msm_ipc_router_recv_from failed - ret: %d\n",
2756 __func__, ret);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002757 return ret;
2758 }
2759
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002760 *data = msm_ipc_router_skb_to_buf(pkt->pkt_fragment_q, ret);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002761 if (!(*data))
2762 pr_err("%s: Buf conversion failed\n", __func__);
2763
2764 *len = ret;
Karthikeyan Ramasubramanian6e2c9a62013-04-16 23:12:44 -06002765 release_pkt(pkt);
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002766 return 0;
2767}
2768
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002769struct msm_ipc_port *msm_ipc_router_create_port(
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002770 void (*notify)(unsigned event, void *priv),
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002771 void *priv)
2772{
2773 struct msm_ipc_port *port_ptr;
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -06002774 int ret;
2775
2776 ret = wait_for_completion_interruptible(&msm_ipc_local_router_up);
2777 if (ret < 0) {
2778 pr_err("%s: Error waiting for local router\n", __func__);
2779 return NULL;
2780 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002781
2782 port_ptr = msm_ipc_router_create_raw_port(NULL, notify, priv);
2783 if (!port_ptr)
2784 pr_err("%s: port_ptr alloc failed\n", __func__);
2785
2786 return port_ptr;
2787}
2788
2789int msm_ipc_router_close_port(struct msm_ipc_port *port_ptr)
2790{
2791 union rr_control_msg msg;
2792 struct rr_packet *pkt, *temp_pkt;
2793 struct msm_ipc_server *server;
2794
2795 if (!port_ptr)
2796 return -EINVAL;
2797
2798 if (port_ptr->type == SERVER_PORT || port_ptr->type == CLIENT_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002799 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002800 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002801 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002802
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002803 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramanian45008a62013-07-19 15:58:38 -06002804 memset(&msg, 0, sizeof(msg));
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002805 msg.cmd = IPC_ROUTER_CTRL_CMD_REMOVE_SERVER;
2806 msg.srv.service = port_ptr->port_name.service;
2807 msg.srv.instance = port_ptr->port_name.instance;
2808 msg.srv.node_id = port_ptr->this_port.node_id;
2809 msg.srv.port_id = port_ptr->this_port.port_id;
2810 RR("x REMOVE_SERVER Name=%d:%08x Id=%d:%08x\n",
2811 msg.srv.service, msg.srv.instance,
2812 msg.srv.node_id, msg.srv.port_id);
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002813 broadcast_ctl_msg(&msg);
2814 broadcast_ctl_msg_locally(&msg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002815 }
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002816
2817 /*
2818 * Server port could have been a client port earlier.
2819 * Send REMOVE_CLIENT message in either case.
2820 */
Karthikeyan Ramasubramanian25e91872013-01-10 17:09:13 -07002821 RR("x REMOVE_CLIENT id=%d:%08x\n",
Karthikeyan Ramasubramaniandc9c4442013-05-02 17:25:54 -06002822 port_ptr->this_port.node_id, port_ptr->this_port.port_id);
2823 msm_ipc_router_send_remove_client(&port_ptr->mode_info,
2824 port_ptr->this_port.node_id,
2825 port_ptr->this_port.port_id);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002826 } else if (port_ptr->type == CONTROL_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002827 down_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramanianbe9954b2012-06-13 12:59:54 -06002828 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002829 up_write(&control_ports_lock_lha5);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002830 } else if (port_ptr->type == IRSC_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002831 down_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002832 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002833 up_write(&local_ports_lock_lha2);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -07002834 signal_irsc_completion();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002835 }
2836
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002837 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002838 list_for_each_entry_safe(pkt, temp_pkt, &port_ptr->port_rx_q, list) {
2839 list_del(&pkt->list);
2840 release_pkt(pkt);
2841 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002842 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002843
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002844 if (port_ptr->type == SERVER_PORT) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002845 down_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002846 server = msm_ipc_router_lookup_server(
2847 port_ptr->port_name.service,
2848 port_ptr->port_name.instance,
2849 port_ptr->this_port.node_id,
2850 port_ptr->this_port.port_id);
2851 if (server)
2852 msm_ipc_router_destroy_server(server,
2853 port_ptr->this_port.node_id,
2854 port_ptr->this_port.port_id);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002855 up_write(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002856 }
2857
Karthikeyan Ramasubramaniandd8c3b52011-11-30 16:26:12 -07002858 wake_lock_destroy(&port_ptr->port_rx_wake_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002859 kfree(port_ptr);
2860 return 0;
2861}
2862
2863int msm_ipc_router_get_curr_pkt_size(struct msm_ipc_port *port_ptr)
2864{
2865 struct rr_packet *pkt;
2866 int rc = 0;
2867
2868 if (!port_ptr)
2869 return -EINVAL;
2870
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002871 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002872 if (!list_empty(&port_ptr->port_rx_q)) {
2873 pkt = list_first_entry(&port_ptr->port_rx_q,
2874 struct rr_packet, list);
2875 rc = pkt->length;
2876 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002877 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002878
2879 return rc;
2880}
2881
2882int msm_ipc_router_bind_control_port(struct msm_ipc_port *port_ptr)
2883{
2884 if (!port_ptr)
2885 return -EINVAL;
2886
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002887 down_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002888 list_del(&port_ptr->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002889 up_write(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002890 port_ptr->type = CONTROL_PORT;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002891 down_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002892 list_add_tail(&port_ptr->list, &control_ports);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002893 up_write(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002894
2895 return 0;
2896}
2897
2898int msm_ipc_router_lookup_server_name(struct msm_ipc_port_name *srv_name,
Flemmard122bb162013-05-22 20:33:47 -07002899#ifdef CONFIG_MACH_HTC
2900 struct msm_ipc_port_addr *srv_info,
2901#else
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002902 struct msm_ipc_server_info *srv_info,
Flemmard122bb162013-05-22 20:33:47 -07002903#endif
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002904 int num_entries_in_array,
2905 uint32_t lookup_mask)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002906{
2907 struct msm_ipc_server *server;
2908 struct msm_ipc_server_port *server_port;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002909 int key, i = 0; /*num_entries_found*/
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002910
2911 if (!srv_name) {
2912 pr_err("%s: Invalid srv_name\n", __func__);
2913 return -EINVAL;
2914 }
2915
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -06002916 if (num_entries_in_array && !srv_info) {
2917 pr_err("%s: srv_info NULL\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002918 return -EINVAL;
2919 }
2920
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002921 down_read(&server_list_lock_lha2);
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002922 if (!lookup_mask)
2923 lookup_mask = 0xFFFFFFFF;
Flemmard122bb162013-05-22 20:33:47 -07002924#ifdef CONFIG_MACH_HTC
2925 for (key = 0; key < SRV_HASH_SIZE; key++)
2926#else
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002927 key = (srv_name->service & (SRV_HASH_SIZE - 1));
Flemmard122bb162013-05-22 20:33:47 -07002928#endif
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002929 list_for_each_entry(server, &server_list[key], list) {
2930 if ((server->name.service != srv_name->service) ||
2931 ((server->name.instance & lookup_mask) !=
2932 srv_name->instance))
2933 continue;
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002934
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002935 list_for_each_entry(server_port,
2936 &server->server_port_list, list) {
2937 if (i < num_entries_in_array) {
2938 srv_info[i].node_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002939 server_port->server_addr.node_id;
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002940 srv_info[i].port_id =
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002941 server_port->server_addr.port_id;
Flemmard122bb162013-05-22 20:33:47 -07002942#ifndef CONFIG_MACH_HTC
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002943 srv_info[i].service = server->name.service;
2944 srv_info[i].instance = server->name.instance;
Flemmard122bb162013-05-22 20:33:47 -07002945#endif
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -06002946 }
Karthikeyan Ramasubramaniana85d09f2012-10-25 15:40:45 -06002947 i++;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002948 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002949 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002950 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002951
2952 return i;
2953}
2954
2955int msm_ipc_router_close(void)
2956{
2957 struct msm_ipc_router_xprt_info *xprt_info, *tmp_xprt_info;
2958
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002959 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002960 list_for_each_entry_safe(xprt_info, tmp_xprt_info,
2961 &xprt_info_list, list) {
Karthikeyan Ramasubramanian8cec5922012-02-16 17:41:58 -07002962 xprt_info->xprt->close(xprt_info->xprt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002963 list_del(&xprt_info->list);
2964 kfree(xprt_info);
2965 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002966 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002967 return 0;
2968}
2969
2970#if defined(CONFIG_DEBUG_FS)
2971static int dump_routing_table(char *buf, int max)
2972{
2973 int i = 0, j;
2974 struct msm_ipc_routing_table_entry *rt_entry;
2975
2976 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002977 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002978 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002979 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002980 i += scnprintf(buf + i, max - i,
2981 "Node Id: 0x%08x\n", rt_entry->node_id);
Karthikeyan Ramasubramanianc1a4e3a2012-09-10 16:10:24 -06002982 if (rt_entry->node_id == IPC_ROUTER_NID_LOCAL) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002983 i += scnprintf(buf + i, max - i,
2984 "XPRT Name: Loopback\n");
2985 i += scnprintf(buf + i, max - i,
2986 "Next Hop: %d\n", rt_entry->node_id);
2987 } else {
2988 i += scnprintf(buf + i, max - i,
2989 "XPRT Name: %s\n",
2990 rt_entry->xprt_info->xprt->name);
2991 i += scnprintf(buf + i, max - i,
2992 "Next Hop: 0x%08x\n",
2993 rt_entry->xprt_info->remote_node_id);
2994 }
2995 i += scnprintf(buf + i, max - i, "\n");
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002996 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002997 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06002998 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002999 }
3000
3001 return i;
3002}
3003
3004static int dump_xprt_info(char *buf, int max)
3005{
3006 int i = 0;
3007 struct msm_ipc_router_xprt_info *xprt_info;
3008
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003009 down_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003010 list_for_each_entry(xprt_info, &xprt_info_list, list) {
3011 i += scnprintf(buf + i, max - i, "XPRT Name: %s\n",
3012 xprt_info->xprt->name);
3013 i += scnprintf(buf + i, max - i, "Link Id: %d\n",
3014 xprt_info->xprt->link_id);
3015 i += scnprintf(buf + i, max - i, "Initialized: %s\n",
3016 (xprt_info->initialized ? "Y" : "N"));
3017 i += scnprintf(buf + i, max - i, "Remote Node Id: 0x%08x\n",
3018 xprt_info->remote_node_id);
3019 i += scnprintf(buf + i, max - i, "\n");
3020 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003021 up_read(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003022
3023 return i;
3024}
3025
3026static int dump_servers(char *buf, int max)
3027{
3028 int i = 0, j;
3029 struct msm_ipc_server *server;
3030 struct msm_ipc_server_port *server_port;
3031
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003032 down_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003033 for (j = 0; j < SRV_HASH_SIZE; j++) {
3034 list_for_each_entry(server, &server_list[j], list) {
3035 list_for_each_entry(server_port,
3036 &server->server_port_list,
3037 list) {
3038 i += scnprintf(buf + i, max - i, "Service: "
3039 "0x%08x\n", server->name.service);
3040 i += scnprintf(buf + i, max - i, "Instance: "
3041 "0x%08x\n", server->name.instance);
3042 i += scnprintf(buf + i, max - i,
3043 "Node_id: 0x%08x\n",
3044 server_port->server_addr.node_id);
3045 i += scnprintf(buf + i, max - i,
3046 "Port_id: 0x%08x\n",
3047 server_port->server_addr.port_id);
3048 i += scnprintf(buf + i, max - i, "\n");
3049 }
3050 }
3051 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003052 up_read(&server_list_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003053
3054 return i;
3055}
3056
3057static int dump_remote_ports(char *buf, int max)
3058{
3059 int i = 0, j, k;
3060 struct msm_ipc_router_remote_port *rport_ptr;
3061 struct msm_ipc_routing_table_entry *rt_entry;
3062
3063 for (j = 0; j < RT_HASH_SIZE; j++) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003064 down_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003065 list_for_each_entry(rt_entry, &routing_table[j], list) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003066 down_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003067 for (k = 0; k < RP_HASH_SIZE; k++) {
3068 list_for_each_entry(rport_ptr,
3069 &rt_entry->remote_port_list[k],
3070 list) {
3071 i += scnprintf(buf + i, max - i,
3072 "Node_id: 0x%08x\n",
3073 rport_ptr->node_id);
3074 i += scnprintf(buf + i, max - i,
3075 "Port_id: 0x%08x\n",
3076 rport_ptr->port_id);
3077 i += scnprintf(buf + i, max - i,
3078 "Quota_cnt: %d\n",
3079 rport_ptr->tx_quota_cnt);
3080 i += scnprintf(buf + i, max - i, "\n");
3081 }
3082 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003083 up_read(&rt_entry->lock_lha4);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003084 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003085 up_read(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003086 }
3087
3088 return i;
3089}
3090
3091static int dump_control_ports(char *buf, int max)
3092{
3093 int i = 0;
3094 struct msm_ipc_port *port_ptr;
3095
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003096 down_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003097 list_for_each_entry(port_ptr, &control_ports, list) {
3098 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
3099 port_ptr->this_port.node_id);
3100 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
3101 port_ptr->this_port.port_id);
3102 i += scnprintf(buf + i, max - i, "\n");
3103 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003104 up_read(&control_ports_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003105
3106 return i;
3107}
3108
3109static int dump_local_ports(char *buf, int max)
3110{
3111 int i = 0, j;
3112 unsigned long flags;
3113 struct msm_ipc_port *port_ptr;
3114
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003115 down_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003116 for (j = 0; j < LP_HASH_SIZE; j++) {
3117 list_for_each_entry(port_ptr, &local_ports[j], list) {
3118 spin_lock_irqsave(&port_ptr->port_lock, flags);
3119 i += scnprintf(buf + i, max - i, "Node_id: 0x%08x\n",
3120 port_ptr->this_port.node_id);
3121 i += scnprintf(buf + i, max - i, "Port_id: 0x%08x\n",
3122 port_ptr->this_port.port_id);
3123 i += scnprintf(buf + i, max - i, "# pkts tx'd %d\n",
3124 port_ptr->num_tx);
3125 i += scnprintf(buf + i, max - i, "# pkts rx'd %d\n",
3126 port_ptr->num_rx);
3127 i += scnprintf(buf + i, max - i, "# bytes tx'd %ld\n",
3128 port_ptr->num_tx_bytes);
3129 i += scnprintf(buf + i, max - i, "# bytes rx'd %ld\n",
3130 port_ptr->num_rx_bytes);
3131 spin_unlock_irqrestore(&port_ptr->port_lock, flags);
3132 i += scnprintf(buf + i, max - i, "\n");
3133 }
3134 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003135 up_read(&local_ports_lock_lha2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003136
3137 return i;
3138}
3139
3140#define DEBUG_BUFMAX 4096
3141static char debug_buffer[DEBUG_BUFMAX];
3142
3143static ssize_t debug_read(struct file *file, char __user *buf,
3144 size_t count, loff_t *ppos)
3145{
3146 int (*fill)(char *buf, int max) = file->private_data;
3147 int bsize = fill(debug_buffer, DEBUG_BUFMAX);
3148 return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize);
3149}
3150
3151static int debug_open(struct inode *inode, struct file *file)
3152{
3153 file->private_data = inode->i_private;
3154 return 0;
3155}
3156
3157static const struct file_operations debug_ops = {
3158 .read = debug_read,
3159 .open = debug_open,
3160};
3161
3162static void debug_create(const char *name, mode_t mode,
3163 struct dentry *dent,
3164 int (*fill)(char *buf, int max))
3165{
3166 debugfs_create_file(name, mode, dent, fill, &debug_ops);
3167}
3168
3169static void debugfs_init(void)
3170{
3171 struct dentry *dent;
3172
3173 dent = debugfs_create_dir("msm_ipc_router", 0);
3174 if (IS_ERR(dent))
3175 return;
3176
3177 debug_create("dump_local_ports", 0444, dent,
3178 dump_local_ports);
3179 debug_create("dump_remote_ports", 0444, dent,
3180 dump_remote_ports);
3181 debug_create("dump_control_ports", 0444, dent,
3182 dump_control_ports);
3183 debug_create("dump_servers", 0444, dent,
3184 dump_servers);
3185 debug_create("dump_xprt_info", 0444, dent,
3186 dump_xprt_info);
3187 debug_create("dump_routing_table", 0444, dent,
3188 dump_routing_table);
3189}
3190
3191#else
3192static void debugfs_init(void) {}
3193#endif
3194
3195static int msm_ipc_router_add_xprt(struct msm_ipc_router_xprt *xprt)
3196{
3197 struct msm_ipc_router_xprt_info *xprt_info;
3198 struct msm_ipc_routing_table_entry *rt_entry;
3199
3200 xprt_info = kmalloc(sizeof(struct msm_ipc_router_xprt_info),
3201 GFP_KERNEL);
3202 if (!xprt_info)
3203 return -ENOMEM;
3204
3205 xprt_info->xprt = xprt;
3206 xprt_info->initialized = 0;
3207 xprt_info->remote_node_id = -1;
3208 INIT_LIST_HEAD(&xprt_info->pkt_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003209 mutex_init(&xprt_info->rx_lock_lhb2);
3210 mutex_init(&xprt_info->tx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003211 wake_lock_init(&xprt_info->wakelock,
3212 WAKE_LOCK_SUSPEND, xprt->name);
3213 xprt_info->need_len = 0;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003214 xprt_info->abort_data_read = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003215 INIT_WORK(&xprt_info->read_data, do_read_data);
3216 INIT_LIST_HEAD(&xprt_info->list);
3217
3218 xprt_info->workqueue = create_singlethread_workqueue(xprt->name);
3219 if (!xprt_info->workqueue) {
3220 kfree(xprt_info);
3221 return -ENOMEM;
3222 }
3223
3224 if (!strcmp(xprt->name, "msm_ipc_router_loopback_xprt")) {
3225 xprt_info->remote_node_id = IPC_ROUTER_NID_LOCAL;
3226 xprt_info->initialized = 1;
3227 }
3228
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003229 down_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003230 list_add_tail(&xprt_info->list, &xprt_info_list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003231 up_write(&xprt_info_list_lock_lha5);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003232
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003233 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003234 if (!routing_table_inited) {
3235 init_routing_table();
3236 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
3237 add_routing_table_entry(rt_entry);
3238 routing_table_inited = 1;
3239 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003240 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003241
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003242 xprt->priv = xprt_info;
3243
3244 return 0;
3245}
3246
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003247static void msm_ipc_router_remove_xprt(struct msm_ipc_router_xprt *xprt)
3248{
3249 struct msm_ipc_router_xprt_info *xprt_info;
3250
3251 if (xprt && xprt->priv) {
3252 xprt_info = xprt->priv;
3253
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003254 mutex_lock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003255 xprt_info->abort_data_read = 1;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003256 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003257
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003258 down_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003259 list_del(&xprt_info->list);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003260 up_write(&xprt_info_list_lock_lha5);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003261
3262 flush_workqueue(xprt_info->workqueue);
3263 destroy_workqueue(xprt_info->workqueue);
3264 wake_lock_destroy(&xprt_info->wakelock);
3265
3266 xprt->priv = 0;
3267 kfree(xprt_info);
3268 }
3269}
3270
3271
3272struct msm_ipc_router_xprt_work {
3273 struct msm_ipc_router_xprt *xprt;
3274 struct work_struct work;
3275};
3276
3277static void xprt_open_worker(struct work_struct *work)
3278{
3279 struct msm_ipc_router_xprt_work *xprt_work =
3280 container_of(work, struct msm_ipc_router_xprt_work, work);
3281
3282 msm_ipc_router_add_xprt(xprt_work->xprt);
3283 kfree(xprt_work);
3284}
3285
3286static void xprt_close_worker(struct work_struct *work)
3287{
3288 struct msm_ipc_router_xprt_work *xprt_work =
3289 container_of(work, struct msm_ipc_router_xprt_work, work);
3290
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003291 msm_ipc_cleanup_routing_table(xprt_work->xprt->priv);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003292 msm_ipc_router_remove_xprt(xprt_work->xprt);
Zaheerulla Meera521afa2013-06-19 16:54:44 +05303293 xprt_work->xprt->sft_close_done(xprt_work->xprt);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003294 kfree(xprt_work);
3295}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003296
3297void msm_ipc_router_xprt_notify(struct msm_ipc_router_xprt *xprt,
3298 unsigned event,
3299 void *data)
3300{
3301 struct msm_ipc_router_xprt_info *xprt_info = xprt->priv;
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003302 struct msm_ipc_router_xprt_work *xprt_work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003303 struct rr_packet *pkt;
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003304 unsigned long ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003305
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003306 if (!msm_ipc_router_workqueue) {
3307 ret = wait_for_completion_timeout(&msm_ipc_local_router_up,
3308 IPC_ROUTER_INIT_TIMEOUT);
3309 if (!ret || !msm_ipc_router_workqueue) {
3310 pr_err("%s: IPC Router not initialized\n", __func__);
3311 return;
3312 }
3313 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003314
3315 switch (event) {
3316 case IPC_ROUTER_XPRT_EVENT_OPEN:
3317 D("open event for '%s'\n", xprt->name);
3318 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
3319 GFP_ATOMIC);
Karthikeyan Ramasubramanianc51456c2013-05-16 15:51:29 -06003320 if (xprt_work) {
3321 xprt_work->xprt = xprt;
3322 INIT_WORK(&xprt_work->work, xprt_open_worker);
3323 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
3324 } else {
3325 pr_err("%s: malloc failure - Couldn't notify OPEN event",
3326 __func__);
3327 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003328 break;
3329
3330 case IPC_ROUTER_XPRT_EVENT_CLOSE:
3331 D("close event for '%s'\n", xprt->name);
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003332 xprt_work = kmalloc(sizeof(struct msm_ipc_router_xprt_work),
3333 GFP_ATOMIC);
Karthikeyan Ramasubramanianc51456c2013-05-16 15:51:29 -06003334 if (xprt_work) {
3335 xprt_work->xprt = xprt;
3336 INIT_WORK(&xprt_work->work, xprt_close_worker);
3337 queue_work(msm_ipc_router_workqueue, &xprt_work->work);
3338 } else {
3339 pr_err("%s: malloc failure - Couldn't notify CLOSE event",
3340 __func__);
3341 }
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003342 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003343 }
3344
3345 if (!data)
3346 return;
3347
3348 while (!xprt_info) {
3349 msleep(100);
3350 xprt_info = xprt->priv;
3351 }
3352
3353 pkt = clone_pkt((struct rr_packet *)data);
3354 if (!pkt)
3355 return;
3356
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003357 mutex_lock(&xprt_info->rx_lock_lhb2);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003358 list_add_tail(&pkt->list, &xprt_info->pkt_list);
3359 wake_lock(&xprt_info->wakelock);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003360 mutex_unlock(&xprt_info->rx_lock_lhb2);
Karthikeyan Ramasubramanian872ecd82012-07-25 11:07:48 -06003361 queue_work(xprt_info->workqueue, &xprt_info->read_data);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003362}
3363
3364static int __init msm_ipc_router_init(void)
3365{
3366 int i, ret;
3367 struct msm_ipc_routing_table_entry *rt_entry;
3368
3369 msm_ipc_router_debug_mask |= SMEM_LOG;
Karthikeyan Ramasubramanian682ebcc2013-03-22 10:47:20 -06003370 ipc_rtr_log_ctxt = ipc_log_context_create(IPC_RTR_LOG_PAGES,
3371 "ipc_router");
3372 if (!ipc_rtr_log_ctxt)
3373 pr_err("%s: Unable to create IPC logging for IPC RTR",
3374 __func__);
3375
Karthikeyan Ramasubramanianff6fbae2011-06-09 11:13:19 -06003376 msm_ipc_router_workqueue =
3377 create_singlethread_workqueue("msm_ipc_router");
3378 if (!msm_ipc_router_workqueue)
3379 return -ENOMEM;
3380
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003381 debugfs_init();
3382
3383 for (i = 0; i < SRV_HASH_SIZE; i++)
3384 INIT_LIST_HEAD(&server_list[i]);
3385
3386 for (i = 0; i < LP_HASH_SIZE; i++)
3387 INIT_LIST_HEAD(&local_ports[i]);
3388
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003389 down_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003390 if (!routing_table_inited) {
3391 init_routing_table();
3392 rt_entry = alloc_routing_table_entry(IPC_ROUTER_NID_LOCAL);
3393 add_routing_table_entry(rt_entry);
3394 routing_table_inited = 1;
3395 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -06003396 up_write(&routing_table_lock_lha3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003397
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003398 ret = msm_ipc_router_init_sockets();
3399 if (ret < 0)
3400 pr_err("%s: Init sockets failed\n", __func__);
3401
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06003402 ret = msm_ipc_router_security_init();
3403 if (ret < 0)
3404 pr_err("%s: Security Init failed\n", __func__);
3405
Karthikeyan Ramasubramanian4af9f7c2011-09-30 15:55:18 -06003406 complete_all(&msm_ipc_local_router_up);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003407 return ret;
3408}
3409
3410module_init(msm_ipc_router_init);
3411MODULE_DESCRIPTION("MSM IPC Router");
3412MODULE_LICENSE("GPL v2");