/* arch/arm/mach-msm/smd_rpcrouter.c * * Copyright (C) 2007 Google, Inc. * Copyright (c) 2007-2011, The Linux Foundation. All rights reserved. * Author: San Mehat * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ /* TODO: handle cases where smd_write() will tempfail due to full fifo */ /* TODO: thread priority? schedule a work to bump it? */ /* TODO: maybe make server_list_lock a mutex */ /* TODO: pool fragments to avoid kmalloc/kfree churn */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smd_rpcrouter.h" #include "modem_notifier.h" #include "smd_rpc_sym.h" #include "smd_private.h" enum { SMEM_LOG = 1U << 0, RTR_DBG = 1U << 1, R2R_MSG = 1U << 2, R2R_RAW = 1U << 3, RPC_MSG = 1U << 4, NTFY_MSG = 1U << 5, RAW_PMR = 1U << 6, RAW_PMW = 1U << 7, R2R_RAW_HDR = 1U << 8, }; static int msm_rpc_connect_timeout_ms; module_param_named(connect_timeout, msm_rpc_connect_timeout_ms, int, S_IRUGO | S_IWUSR | S_IWGRP); static int smd_rpcrouter_debug_mask; module_param_named(debug_mask, smd_rpcrouter_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP); #define DIAG(x...) printk(KERN_ERR "[RR] ERROR " x) #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) #define D(x...) do { \ if (smd_rpcrouter_debug_mask & RTR_DBG) \ printk(KERN_ERR x); \ } while (0) #define RR(x...) do { \ if (smd_rpcrouter_debug_mask & R2R_MSG) \ printk(KERN_ERR "[RR] "x); \ } while (0) #define RAW(x...) do { \ if (smd_rpcrouter_debug_mask & R2R_RAW) \ printk(KERN_ERR "[RAW] "x); \ } while (0) #define RAW_HDR(x...) do { \ if (smd_rpcrouter_debug_mask & R2R_RAW_HDR) \ printk(KERN_ERR "[HDR] "x); \ } while (0) #define RAW_PMR(x...) do { \ if (smd_rpcrouter_debug_mask & RAW_PMR) \ printk(KERN_ERR "[PMR] "x); \ } while (0) #define RAW_PMR_NOMASK(x...) do { \ printk(KERN_ERR "[PMR] "x); \ } while (0) #define RAW_PMW(x...) do { \ if (smd_rpcrouter_debug_mask & RAW_PMW) \ printk(KERN_ERR "[PMW] "x); \ } while (0) #define RAW_PMW_NOMASK(x...) do { \ printk(KERN_ERR "[PMW] "x); \ } while (0) #define IO(x...) do { \ if (smd_rpcrouter_debug_mask & RPC_MSG) \ printk(KERN_ERR "[RPC] "x); \ } while (0) #define NTFY(x...) do { \ if (smd_rpcrouter_debug_mask & NTFY_MSG) \ printk(KERN_ERR "[NOTIFY] "x); \ } while (0) #else #define D(x...) do { } while (0) #define RR(x...) do { } while (0) #define RAW(x...) do { } while (0) #define RAW_HDR(x...) do { } while (0) #define RAW_PMR(x...) do { } while (0) #define RAW_PMR_NO_MASK(x...) do { } while (0) #define RAW_PMW(x...) do { } while (0) #define RAW_PMW_NO_MASK(x...) do { } while (0) #define IO(x...) do { } while (0) #define NTFY(x...) do { } while (0) #endif static LIST_HEAD(local_endpoints); static LIST_HEAD(remote_endpoints); static LIST_HEAD(server_list); static wait_queue_head_t newserver_wait; static wait_queue_head_t subsystem_restart_wait; static DEFINE_SPINLOCK(local_endpoints_lock); static DEFINE_SPINLOCK(remote_endpoints_lock); static DEFINE_SPINLOCK(server_list_lock); static LIST_HEAD(rpc_board_dev_list); static DEFINE_SPINLOCK(rpc_board_dev_list_lock); static struct workqueue_struct *rpcrouter_workqueue; static atomic_t next_xid = ATOMIC_INIT(1); static atomic_t pm_mid = ATOMIC_INIT(1); static void do_read_data(struct work_struct *work); static void do_create_pdevs(struct work_struct *work); static void do_create_rpcrouter_pdev(struct work_struct *work); static DECLARE_WORK(work_create_pdevs, do_create_pdevs); static DECLARE_WORK(work_create_rpcrouter_pdev, do_create_rpcrouter_pdev); #define RR_STATE_IDLE 0 #define RR_STATE_HEADER 1 #define RR_STATE_BODY 2 #define RR_STATE_ERROR 3 /* State for remote ep following restart */ #define RESTART_QUOTA_ABORT 1 struct rr_context { struct rr_packet *pkt; uint8_t *ptr; uint32_t state; /* current assembly state */ uint32_t count; /* bytes needed in this state */ }; struct rr_context the_rr_context; struct rpc_board_dev_info { struct list_head list; struct rpc_board_dev *dev; }; static struct platform_device rpcrouter_pdev = { .name = "oncrpc_router", .id = -1, }; struct rpcrouter_xprt_info { struct list_head list; struct rpcrouter_xprt *xprt; int remote_pid; uint32_t initialized; wait_queue_head_t read_wait; struct wake_lock wakelock; spinlock_t lock; uint32_t need_len; struct work_struct read_data; struct workqueue_struct *workqueue; int abort_data_read; unsigned char r2r_buf[RPCROUTER_MSGSIZE_MAX]; }; static LIST_HEAD(xprt_info_list); static DEFINE_MUTEX(xprt_info_list_lock); DECLARE_COMPLETION(rpc_remote_router_up); static atomic_t pending_close_count = ATOMIC_INIT(0); /* * Search for transport (xprt) that matches the provided PID. * * Note: The calling function must ensure that the mutex * xprt_info_list_lock is locked when this function * is called. * * @remote_pid Remote PID for the transport * * @returns Pointer to transport or NULL if not found */ static struct rpcrouter_xprt_info *rpcrouter_get_xprt_info(uint32_t remote_pid) { struct rpcrouter_xprt_info *xprt_info; list_for_each_entry(xprt_info, &xprt_info_list, list) { if (xprt_info->remote_pid == remote_pid) return xprt_info; } return NULL; } static int rpcrouter_send_control_msg(struct rpcrouter_xprt_info *xprt_info, union rr_control_msg *msg) { struct rr_header hdr; unsigned long flags = 0; int need; if (xprt_info->remote_pid == RPCROUTER_PID_LOCAL) return 0; if (!(msg->cmd == RPCROUTER_CTRL_CMD_HELLO) && !xprt_info->initialized) { printk(KERN_ERR "rpcrouter_send_control_msg(): Warning, " "router not initialized\n"); return -EINVAL; } hdr.version = RPCROUTER_VERSION; hdr.type = msg->cmd; hdr.src_pid = RPCROUTER_PID_LOCAL; hdr.src_cid = RPCROUTER_ROUTER_ADDRESS; hdr.confirm_rx = 0; hdr.size = sizeof(*msg); hdr.dst_pid = xprt_info->remote_pid; hdr.dst_cid = RPCROUTER_ROUTER_ADDRESS; /* TODO: what if channel is full? */ need = sizeof(hdr) + hdr.size; spin_lock_irqsave(&xprt_info->lock, flags); while (xprt_info->xprt->write_avail() < need) { spin_unlock_irqrestore(&xprt_info->lock, flags); msleep(250); spin_lock_irqsave(&xprt_info->lock, flags); } xprt_info->xprt->write(&hdr, sizeof(hdr), HEADER); xprt_info->xprt->write(msg, hdr.size, PAYLOAD); spin_unlock_irqrestore(&xprt_info->lock, flags); return 0; } static void modem_reset_cleanup(struct rpcrouter_xprt_info *xprt_info) { struct msm_rpc_endpoint *ept; struct rr_remote_endpoint *r_ept; struct rr_packet *pkt, *tmp_pkt; struct rr_fragment *frag, *next; struct msm_rpc_reply *reply, *reply_tmp; unsigned long flags; spin_lock_irqsave(&local_endpoints_lock, flags); /* remove all partial packets received */ list_for_each_entry(ept, &local_endpoints, list) { RR("%s EPT DST PID %x, remote_pid:%d\n", __func__, ept->dst_pid, xprt_info->remote_pid); if (xprt_info->remote_pid != ept->dst_pid) continue; D("calling teardown cb %p\n", ept->cb_restart_teardown); if (ept->cb_restart_teardown) ept->cb_restart_teardown(ept->client_data); ept->do_setup_notif = 1; /* remove replies */ spin_lock(&ept->reply_q_lock); list_for_each_entry_safe(reply, reply_tmp, &ept->reply_pend_q, list) { list_del(&reply->list); kfree(reply); } list_for_each_entry_safe(reply, reply_tmp, &ept->reply_avail_q, list) { list_del(&reply->list); kfree(reply); } ept->reply_cnt = 0; spin_unlock(&ept->reply_q_lock); /* Set restart state for local ep */ RR("EPT:0x%p, State %d RESTART_PEND_NTFY_SVR " "PROG:0x%08x VERS:0x%08x\n", ept, ept->restart_state, be32_to_cpu(ept->dst_prog), be32_to_cpu(ept->dst_vers)); spin_lock(&ept->restart_lock); ept->restart_state = RESTART_PEND_NTFY_SVR; /* remove incomplete packets */ spin_lock(&ept->incomplete_lock); list_for_each_entry_safe(pkt, tmp_pkt, &ept->incomplete, list) { list_del(&pkt->list); frag = pkt->first; while (frag != NULL) { next = frag->next; kfree(frag); frag = next; } kfree(pkt); } spin_unlock(&ept->incomplete_lock); /* remove all completed packets waiting to be read */ spin_lock(&ept->read_q_lock); list_for_each_entry_safe(pkt, tmp_pkt, &ept->read_q, list) { list_del(&pkt->list); frag = pkt->first; while (frag != NULL) { next = frag->next; kfree(frag); frag = next; } kfree(pkt); } spin_unlock(&ept->read_q_lock); spin_unlock(&ept->restart_lock); wake_up(&ept->wait_q); } spin_unlock_irqrestore(&local_endpoints_lock, flags); /* Unblock endpoints waiting for quota ack*/ spin_lock_irqsave(&remote_endpoints_lock, flags); list_for_each_entry(r_ept, &remote_endpoints, list) { spin_lock(&r_ept->quota_lock); r_ept->quota_restart_state = RESTART_QUOTA_ABORT; RR("Set STATE_PENDING PID:0x%08x CID:0x%08x \n", r_ept->pid, r_ept->cid); spin_unlock(&r_ept->quota_lock); wake_up(&r_ept->quota_wait); } spin_unlock_irqrestore(&remote_endpoints_lock, flags); } static void modem_reset_startup(struct rpcrouter_xprt_info *xprt_info) { struct msm_rpc_endpoint *ept; unsigned long flags; spin_lock_irqsave(&local_endpoints_lock, flags); /* notify all endpoints that we are coming back up */ list_for_each_entry(ept, &local_endpoints, list) { RR("%s EPT DST PID %x, remote_pid:%d\n", __func__, ept->dst_pid, xprt_info->remote_pid); if (xprt_info->remote_pid != ept->dst_pid) continue; D("calling setup cb %d:%p\n", ept->do_setup_notif, ept->cb_restart_setup); if (ept->do_setup_notif && ept->cb_restart_setup) ept->cb_restart_setup(ept->client_data); ept->do_setup_notif = 0; } spin_unlock_irqrestore(&local_endpoints_lock, flags); } /* * Blocks and waits for endpoint if a reset is in progress. * * @returns * ENETRESET Reset is in progress and a notification needed * ERESTARTSYS Signal occurred * 0 Reset is not in progress */ static int wait_for_restart_and_notify(struct msm_rpc_endpoint *ept) { unsigned long flags; int ret = 0; DEFINE_WAIT(__wait); for (;;) { prepare_to_wait(&ept->restart_wait, &__wait, TASK_INTERRUPTIBLE); spin_lock_irqsave(&ept->restart_lock, flags); if (ept->restart_state == RESTART_NORMAL) { spin_unlock_irqrestore(&ept->restart_lock, flags); break; } else if (ept->restart_state & RESTART_PEND_NTFY) { ept->restart_state &= ~RESTART_PEND_NTFY; spin_unlock_irqrestore(&ept->restart_lock, flags); ret = -ENETRESET; break; } if (signal_pending(current) && ((!(ept->flags & MSM_RPC_UNINTERRUPTIBLE)))) { spin_unlock_irqrestore(&ept->restart_lock, flags); ret = -ERESTARTSYS; break; } spin_unlock_irqrestore(&ept->restart_lock, flags); schedule(); } finish_wait(&ept->restart_wait, &__wait); return ret; } static struct rr_server *rpcrouter_create_server(uint32_t pid, uint32_t cid, uint32_t prog, uint32_t ver) { struct rr_server *server; unsigned long flags; int rc; server = kmalloc(sizeof(struct rr_server), GFP_KERNEL); if (!server) return ERR_PTR(-ENOMEM); memset(server, 0, sizeof(struct rr_server)); server->pid = pid; server->cid = cid; server->prog = prog; server->vers = ver; spin_lock_irqsave(&server_list_lock, flags); list_add_tail(&server->list, &server_list); spin_unlock_irqrestore(&server_list_lock, flags); rc = msm_rpcrouter_create_server_cdev(server); if (rc < 0) goto out_fail; return server; out_fail: spin_lock_irqsave(&server_list_lock, flags); list_del(&server->list); spin_unlock_irqrestore(&server_list_lock, flags); kfree(server); return ERR_PTR(rc); } static void rpcrouter_destroy_server(struct rr_server *server) { unsigned long flags; spin_lock_irqsave(&server_list_lock, flags); list_del(&server->list); spin_unlock_irqrestore(&server_list_lock, flags); device_destroy(msm_rpcrouter_class, server->device_number); kfree(server); } int msm_rpc_add_board_dev(struct rpc_board_dev *devices, int num) { unsigned long flags; struct rpc_board_dev_info *board_info; int i; for (i = 0; i < num; i++) { board_info = kzalloc(sizeof(struct rpc_board_dev_info), GFP_KERNEL); if (!board_info) return -ENOMEM; board_info->dev = &devices[i]; D("%s: adding program %x\n", __func__, board_info->dev->prog); spin_lock_irqsave(&rpc_board_dev_list_lock, flags); list_add_tail(&board_info->list, &rpc_board_dev_list); spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags); } return 0; } EXPORT_SYMBOL(msm_rpc_add_board_dev); static void rpcrouter_register_board_dev(struct rr_server *server) { struct rpc_board_dev_info *board_info; unsigned long flags; int rc; spin_lock_irqsave(&rpc_board_dev_list_lock, flags); list_for_each_entry(board_info, &rpc_board_dev_list, list) { if (server->prog == board_info->dev->prog) { D("%s: registering device %x\n", __func__, board_info->dev->prog); list_del(&board_info->list); rc = platform_device_register(&board_info->dev->pdev); if (rc) pr_err("%s: board dev register failed %d\n", __func__, rc); kfree(board_info); break; } } spin_unlock_irqrestore(&rpc_board_dev_list_lock, flags); } static struct rr_server *rpcrouter_lookup_server(uint32_t prog, uint32_t ver) { struct rr_server *server; unsigned long flags; spin_lock_irqsave(&server_list_lock, flags); list_for_each_entry(server, &server_list, list) { if (server->prog == prog && server->vers == ver) { spin_unlock_irqrestore(&server_list_lock, flags); return server; } } spin_unlock_irqrestore(&server_list_lock, flags); return NULL; } static struct rr_server *rpcrouter_lookup_server_by_dev(dev_t dev) { struct rr_server *server; unsigned long flags; spin_lock_irqsave(&server_list_lock, flags); list_for_each_entry(server, &server_list, list) { if (server->device_number == dev) { spin_unlock_irqrestore(&server_list_lock, flags); return server; } } spin_unlock_irqrestore(&server_list_lock, flags); return NULL; } struct msm_rpc_endpoint *msm_rpcrouter_create_local_endpoint(dev_t dev) { struct msm_rpc_endpoint *ept; unsigned long flags; ept = kmalloc(sizeof(struct msm_rpc_endpoint), GFP_KERNEL); if (!ept) return NULL; memset(ept, 0, sizeof(struct msm_rpc_endpoint)); ept->cid = (uint32_t) ept; ept->pid = RPCROUTER_PID_LOCAL; ept->dev = dev; if ((dev != msm_rpcrouter_devno) && (dev != MKDEV(0, 0))) { struct rr_server *srv; /* * This is a userspace client which opened * a program/ver devicenode. Bind the client * to that destination */ srv = rpcrouter_lookup_server_by_dev(dev); /* TODO: bug? really? */ BUG_ON(!srv); ept->dst_pid = srv->pid; ept->dst_cid = srv->cid; ept->dst_prog = cpu_to_be32(srv->prog); ept->dst_vers = cpu_to_be32(srv->vers); } else { /* mark not connected */ ept->dst_pid = 0xffffffff; } init_waitqueue_head(&ept->wait_q); INIT_LIST_HEAD(&ept->read_q); spin_lock_init(&ept->read_q_lock); INIT_LIST_HEAD(&ept->reply_avail_q); INIT_LIST_HEAD(&ept->reply_pend_q); spin_lock_init(&ept->reply_q_lock); spin_lock_init(&ept->restart_lock); init_waitqueue_head(&ept->restart_wait); ept->restart_state = RESTART_NORMAL; wake_lock_init(&ept->read_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_read"); wake_lock_init(&ept->reply_q_wake_lock, WAKE_LOCK_SUSPEND, "rpc_reply"); INIT_LIST_HEAD(&ept->incomplete); spin_lock_init(&ept->incomplete_lock); spin_lock_irqsave(&local_endpoints_lock, flags); list_add_tail(&ept->list, &local_endpoints); spin_unlock_irqrestore(&local_endpoints_lock, flags); return ept; } int msm_rpcrouter_destroy_local_endpoint(struct msm_rpc_endpoint *ept) { int rc; union rr_control_msg msg; struct msm_rpc_reply *reply, *reply_tmp; unsigned long flags; struct rpcrouter_xprt_info *xprt_info; /* Endpoint with dst_pid = 0xffffffff corresponds to that of ** router port. So don't send a REMOVE CLIENT message while ** destroying it.*/ spin_lock_irqsave(&local_endpoints_lock, flags); list_del(&ept->list); spin_unlock_irqrestore(&local_endpoints_lock, flags); if (ept->dst_pid != 0xffffffff) { msg.cmd = RPCROUTER_CTRL_CMD_REMOVE_CLIENT; msg.cli.pid = ept->pid; msg.cli.cid = ept->cid; RR("x REMOVE_CLIENT id=%d:%08x\n", ept->pid, ept->cid); mutex_lock(&xprt_info_list_lock); list_for_each_entry(xprt_info, &xprt_info_list, list) { rc = rpcrouter_send_control_msg(xprt_info, &msg); if (rc < 0) { mutex_unlock(&xprt_info_list_lock); return rc; } } mutex_unlock(&xprt_info_list_lock); } /* Free replies */ spin_lock_irqsave(&ept->reply_q_lock, flags); list_for_each_entry_safe(reply, reply_tmp, &ept->reply_pend_q, list) { list_del(&reply->list); kfree(reply); } list_for_each_entry_safe(reply, reply_tmp, &ept->reply_avail_q, list) { list_del(&reply->list); kfree(reply); } spin_unlock_irqrestore(&ept->reply_q_lock, flags); wake_lock_destroy(&ept->read_q_wake_lock); wake_lock_destroy(&ept->reply_q_wake_lock); kfree(ept); return 0; } static int rpcrouter_create_remote_endpoint(uint32_t pid, uint32_t cid) { struct rr_remote_endpoint *new_c; unsigned long flags; new_c = kmalloc(sizeof(struct rr_remote_endpoint), GFP_KERNEL); if (!new_c) return -ENOMEM; memset(new_c, 0, sizeof(struct rr_remote_endpoint)); new_c->cid = cid; new_c->pid = pid; init_waitqueue_head(&new_c->quota_wait); spin_lock_init(&new_c->quota_lock); spin_lock_irqsave(&remote_endpoints_lock, flags); list_add_tail(&new_c->list, &remote_endpoints); new_c->quota_restart_state = RESTART_NORMAL; spin_unlock_irqrestore(&remote_endpoints_lock, flags); return 0; } static struct msm_rpc_endpoint *rpcrouter_lookup_local_endpoint(uint32_t cid) { struct msm_rpc_endpoint *ept; list_for_each_entry(ept, &local_endpoints, list) { if (ept->cid == cid) return ept; } return NULL; } static struct rr_remote_endpoint *rpcrouter_lookup_remote_endpoint(uint32_t pid, uint32_t cid) { struct rr_remote_endpoint *ept; unsigned long flags; spin_lock_irqsave(&remote_endpoints_lock, flags); list_for_each_entry(ept, &remote_endpoints, list) { if ((ept->pid == pid) && (ept->cid == cid)) { spin_unlock_irqrestore(&remote_endpoints_lock, flags); return ept; } } spin_unlock_irqrestore(&remote_endpoints_lock, flags); return NULL; } static void handle_server_restart(struct rr_server *server, uint32_t pid, uint32_t cid, uint32_t prog, uint32_t vers) { struct rr_remote_endpoint *r_ept; struct msm_rpc_endpoint *ept; unsigned long flags; r_ept = rpcrouter_lookup_remote_endpoint(pid, cid); if (r_ept && (r_ept->quota_restart_state != RESTART_NORMAL)) { spin_lock_irqsave(&r_ept->quota_lock, flags); r_ept->tx_quota_cntr = 0; r_ept->quota_restart_state = RESTART_NORMAL; spin_unlock_irqrestore(&r_ept->quota_lock, flags); D(KERN_INFO "rpcrouter: Remote EPT Reset %0x\n", (unsigned int)r_ept); wake_up(&r_ept->quota_wait); } spin_lock_irqsave(&local_endpoints_lock, flags); list_for_each_entry(ept, &local_endpoints, list) { if ((be32_to_cpu(ept->dst_prog) == prog) && (be32_to_cpu(ept->dst_vers) == vers) && (ept->restart_state & RESTART_PEND_SVR)) { spin_lock(&ept->restart_lock); ept->restart_state &= ~RESTART_PEND_SVR; spin_unlock(&ept->restart_lock); D("rpcrouter: Local EPT Reset %08x:%08x \n", prog, vers); wake_up(&ept->restart_wait); wake_up(&ept->wait_q); } } spin_unlock_irqrestore(&local_endpoints_lock, flags); } static int process_control_msg(struct rpcrouter_xprt_info *xprt_info, union rr_control_msg *msg, int len) { union rr_control_msg ctl; struct rr_server *server; struct rr_remote_endpoint *r_ept; int rc = 0; unsigned long flags; static int first = 1; if (len != sizeof(*msg)) { RR(KERN_ERR "rpcrouter: r2r msg size %d != %d\n", len, sizeof(*msg)); return -EINVAL; } switch (msg->cmd) { case RPCROUTER_CTRL_CMD_HELLO: RR("o HELLO PID %d\n", xprt_info->remote_pid); memset(&ctl, 0, sizeof(ctl)); ctl.cmd = RPCROUTER_CTRL_CMD_HELLO; rpcrouter_send_control_msg(xprt_info, &ctl); xprt_info->initialized = 1; /* Send list of servers one at a time */ ctl.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; /* TODO: long time to hold a spinlock... */ spin_lock_irqsave(&server_list_lock, flags); list_for_each_entry(server, &server_list, list) { if (server->pid != RPCROUTER_PID_LOCAL) continue; ctl.srv.pid = server->pid; ctl.srv.cid = server->cid; ctl.srv.prog = server->prog; ctl.srv.vers = server->vers; RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", server->pid, server->cid, server->prog, server->vers); rpcrouter_send_control_msg(xprt_info, &ctl); } spin_unlock_irqrestore(&server_list_lock, flags); if (first) { first = 0; queue_work(rpcrouter_workqueue, &work_create_rpcrouter_pdev); } break; case RPCROUTER_CTRL_CMD_RESUME_TX: RR("o RESUME_TX id=%d:%08x\n", msg->cli.pid, msg->cli.cid); r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.pid, msg->cli.cid); if (!r_ept) { printk(KERN_ERR "rpcrouter: Unable to resume client\n"); break; } spin_lock_irqsave(&r_ept->quota_lock, flags); r_ept->tx_quota_cntr = 0; spin_unlock_irqrestore(&r_ept->quota_lock, flags); wake_up(&r_ept->quota_wait); break; case RPCROUTER_CTRL_CMD_NEW_SERVER: if (msg->srv.vers == 0) { pr_err( "rpcrouter: Server create rejected, version = 0, " "program = %08x\n", msg->srv.prog); break; } RR("o NEW_SERVER id=%d:%08x prog=%08x:%08x\n", msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); if (!server) { server = rpcrouter_create_server( msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); if (!server) return -ENOMEM; /* * XXX: Verify that its okay to add the * client to our remote client list * if we get a NEW_SERVER notification */ if (!rpcrouter_lookup_remote_endpoint(msg->srv.pid, msg->srv.cid)) { rc = rpcrouter_create_remote_endpoint( msg->srv.pid, msg->srv.cid); if (rc < 0) printk(KERN_ERR "rpcrouter:Client create" "error (%d)\n", rc); } rpcrouter_register_board_dev(server); schedule_work(&work_create_pdevs); wake_up(&newserver_wait); } else { if ((server->pid == msg->srv.pid) && (server->cid == msg->srv.cid)) { handle_server_restart(server, msg->srv.pid, msg->srv.cid, msg->srv.prog, msg->srv.vers); } else { server->pid = msg->srv.pid; server->cid = msg->srv.cid; } } break; case RPCROUTER_CTRL_CMD_REMOVE_SERVER: RR("o REMOVE_SERVER prog=%08x:%d\n", msg->srv.prog, msg->srv.vers); server = rpcrouter_lookup_server(msg->srv.prog, msg->srv.vers); if (server) rpcrouter_destroy_server(server); break; case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: RR("o REMOVE_CLIENT id=%d:%08x\n", msg->cli.pid, msg->cli.cid); if (msg->cli.pid == RPCROUTER_PID_LOCAL) { printk(KERN_ERR "rpcrouter: Denying remote removal of " "local client\n"); break; } r_ept = rpcrouter_lookup_remote_endpoint(msg->cli.pid, msg->cli.cid); if (r_ept) { spin_lock_irqsave(&remote_endpoints_lock, flags); list_del(&r_ept->list); spin_unlock_irqrestore(&remote_endpoints_lock, flags); kfree(r_ept); } /* Notify local clients of this event */ printk(KERN_ERR "rpcrouter: LOCAL NOTIFICATION NOT IMP\n"); rc = -ENOSYS; break; case RPCROUTER_CTRL_CMD_PING: /* No action needed for ping messages received */ RR("o PING\n"); break; default: RR("o UNKNOWN(%08x)\n", msg->cmd); rc = -ENOSYS; } return rc; } static void do_create_rpcrouter_pdev(struct work_struct *work) { D("%s: modem rpc router up\n", __func__); platform_device_register(&rpcrouter_pdev); complete_all(&rpc_remote_router_up); } static void do_create_pdevs(struct work_struct *work) { unsigned long flags; struct rr_server *server; /* TODO: race if destroyed while being registered */ spin_lock_irqsave(&server_list_lock, flags); list_for_each_entry(server, &server_list, list) { if (server->pid != RPCROUTER_PID_LOCAL) { if (server->pdev_name[0] == 0) { sprintf(server->pdev_name, "rs%.8x", server->prog); spin_unlock_irqrestore(&server_list_lock, flags); msm_rpcrouter_create_server_pdev(server); schedule_work(&work_create_pdevs); return; } } } spin_unlock_irqrestore(&server_list_lock, flags); } static void *rr_malloc(unsigned sz) { void *ptr = kmalloc(sz, GFP_KERNEL); if (ptr) return ptr; printk(KERN_ERR "rpcrouter: kmalloc of %d failed, retrying...\n", sz); do { ptr = kmalloc(sz, GFP_KERNEL); } while (!ptr); return ptr; } static int rr_read(struct rpcrouter_xprt_info *xprt_info, void *data, uint32_t len) { int rc; unsigned long flags; while (!xprt_info->abort_data_read) { spin_lock_irqsave(&xprt_info->lock, flags); if (xprt_info->xprt->read_avail() >= len) { rc = xprt_info->xprt->read(data, len); spin_unlock_irqrestore(&xprt_info->lock, flags); if (rc == len && !xprt_info->abort_data_read) return 0; else return -EIO; } xprt_info->need_len = len; wake_unlock(&xprt_info->wakelock); spin_unlock_irqrestore(&xprt_info->lock, flags); wait_event(xprt_info->read_wait, xprt_info->xprt->read_avail() >= len || xprt_info->abort_data_read); } return -EIO; } #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) static char *type_to_str(int i) { switch (i) { case RPCROUTER_CTRL_CMD_DATA: return "data "; case RPCROUTER_CTRL_CMD_HELLO: return "hello "; case RPCROUTER_CTRL_CMD_BYE: return "bye "; case RPCROUTER_CTRL_CMD_NEW_SERVER: return "new_srvr"; case RPCROUTER_CTRL_CMD_REMOVE_SERVER: return "rmv_srvr"; case RPCROUTER_CTRL_CMD_REMOVE_CLIENT: return "rmv_clnt"; case RPCROUTER_CTRL_CMD_RESUME_TX: return "resum_tx"; case RPCROUTER_CTRL_CMD_EXIT: return "cmd_exit"; default: return "invalid"; } } #endif static void do_read_data(struct work_struct *work) { struct rr_header hdr; struct rr_packet *pkt; struct rr_fragment *frag; struct msm_rpc_endpoint *ept; #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) struct rpc_request_hdr *rq; #endif uint32_t pm, mid; unsigned long flags; struct rpcrouter_xprt_info *xprt_info = container_of(work, struct rpcrouter_xprt_info, read_data); if (rr_read(xprt_info, &hdr, sizeof(hdr))) goto fail_io; RR("- ver=%d type=%d src=%d:%08x crx=%d siz=%d dst=%d:%08x\n", hdr.version, hdr.type, hdr.src_pid, hdr.src_cid, hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); RAW_HDR("[r rr_h] " "ver=%i,type=%s,src_pid=%08x,src_cid=%08x," "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", hdr.version, type_to_str(hdr.type), hdr.src_pid, hdr.src_cid, hdr.confirm_rx, hdr.size, hdr.dst_pid, hdr.dst_cid); if (hdr.version != RPCROUTER_VERSION) { DIAG("version %d != %d\n", hdr.version, RPCROUTER_VERSION); goto fail_data; } if (hdr.size > RPCROUTER_MSGSIZE_MAX) { DIAG("msg size %d > max %d\n", hdr.size, RPCROUTER_MSGSIZE_MAX); goto fail_data; } if (hdr.dst_cid == RPCROUTER_ROUTER_ADDRESS) { if (xprt_info->remote_pid == -1) { xprt_info->remote_pid = hdr.src_pid; /* do restart notification */ modem_reset_startup(xprt_info); } if (rr_read(xprt_info, xprt_info->r2r_buf, hdr.size)) goto fail_io; process_control_msg(xprt_info, (void *) xprt_info->r2r_buf, hdr.size); goto done; } if (hdr.size < sizeof(pm)) { DIAG("runt packet (no pacmark)\n"); goto fail_data; } if (rr_read(xprt_info, &pm, sizeof(pm))) goto fail_io; hdr.size -= sizeof(pm); frag = rr_malloc(sizeof(*frag)); frag->next = NULL; frag->length = hdr.size; if (rr_read(xprt_info, frag->data, hdr.size)) { kfree(frag); goto fail_io; } #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) if ((smd_rpcrouter_debug_mask & RAW_PMR) && ((pm >> 30 & 0x1) || (pm >> 31 & 0x1))) { uint32_t xid = 0; if (pm >> 30 & 0x1) { rq = (struct rpc_request_hdr *) frag->data; xid = ntohl(rq->xid); } if ((pm >> 31 & 0x1) || (pm >> 30 & 0x1)) RAW_PMR_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i," "len=%3i,dst_cid=%08x\n", xid, pm >> 30 & 0x1, pm >> 31 & 0x1, pm >> 16 & 0xFF, pm & 0xFFFF, hdr.dst_cid); } if (smd_rpcrouter_debug_mask & SMEM_LOG) { rq = (struct rpc_request_hdr *) frag->data; if (rq->xid == 0) smem_log_event(SMEM_LOG_PROC_ID_APPS | RPC_ROUTER_LOG_EVENT_MID_READ, PACMARK_MID(pm), hdr.dst_cid, hdr.src_cid); else smem_log_event(SMEM_LOG_PROC_ID_APPS | RPC_ROUTER_LOG_EVENT_MSG_READ, ntohl(rq->xid), hdr.dst_cid, hdr.src_cid); } #endif spin_lock_irqsave(&local_endpoints_lock, flags); ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); if (!ept) { spin_unlock_irqrestore(&local_endpoints_lock, flags); DIAG("no local ept for cid %08x\n", hdr.dst_cid); kfree(frag); goto done; } /* See if there is already a partial packet that matches our mid * and if so, append this fragment to that packet. */ mid = PACMARK_MID(pm); spin_lock(&ept->incomplete_lock); list_for_each_entry(pkt, &ept->incomplete, list) { if (pkt->mid == mid) { pkt->last->next = frag; pkt->last = frag; pkt->length += frag->length; if (PACMARK_LAST(pm)) { list_del(&pkt->list); spin_unlock(&ept->incomplete_lock); goto packet_complete; } spin_unlock(&ept->incomplete_lock); spin_unlock_irqrestore(&local_endpoints_lock, flags); goto done; } } spin_unlock(&ept->incomplete_lock); spin_unlock_irqrestore(&local_endpoints_lock, flags); /* This mid is new -- create a packet for it, and put it on * the incomplete list if this fragment is not a last fragment, * otherwise put it on the read queue. */ pkt = rr_malloc(sizeof(struct rr_packet)); pkt->first = frag; pkt->last = frag; memcpy(&pkt->hdr, &hdr, sizeof(hdr)); pkt->mid = mid; pkt->length = frag->length; spin_lock_irqsave(&local_endpoints_lock, flags); ept = rpcrouter_lookup_local_endpoint(hdr.dst_cid); if (!ept) { spin_unlock_irqrestore(&local_endpoints_lock, flags); DIAG("no local ept for cid %08x\n", hdr.dst_cid); kfree(frag); kfree(pkt); goto done; } if (!PACMARK_LAST(pm)) { spin_lock(&ept->incomplete_lock); list_add_tail(&pkt->list, &ept->incomplete); spin_unlock(&ept->incomplete_lock); spin_unlock_irqrestore(&local_endpoints_lock, flags); goto done; } packet_complete: spin_lock(&ept->read_q_lock); D("%s: take read lock on ept %p\n", __func__, ept); wake_lock(&ept->read_q_wake_lock); list_add_tail(&pkt->list, &ept->read_q); wake_up(&ept->wait_q); spin_unlock(&ept->read_q_lock); spin_unlock_irqrestore(&local_endpoints_lock, flags); done: if (hdr.confirm_rx) { union rr_control_msg msg; msg.cmd = RPCROUTER_CTRL_CMD_RESUME_TX; msg.cli.pid = hdr.dst_pid; msg.cli.cid = hdr.dst_cid; RR("x RESUME_TX id=%d:%08x\n", msg.cli.pid, msg.cli.cid); rpcrouter_send_control_msg(xprt_info, &msg); #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) if (smd_rpcrouter_debug_mask & SMEM_LOG) smem_log_event(SMEM_LOG_PROC_ID_APPS | RPC_ROUTER_LOG_EVENT_MSG_CFM_SNT, RPCROUTER_PID_LOCAL, hdr.dst_cid, hdr.src_cid); #endif } /* don't requeue if we should be shutting down */ if (!xprt_info->abort_data_read) { queue_work(xprt_info->workqueue, &xprt_info->read_data); return; } D("rpc_router terminating for '%s'\n", xprt_info->xprt->name); fail_io: fail_data: D(KERN_ERR "rpc_router has died for '%s'\n", xprt_info->xprt->name); } void msm_rpc_setup_req(struct rpc_request_hdr *hdr, uint32_t prog, uint32_t vers, uint32_t proc) { memset(hdr, 0, sizeof(struct rpc_request_hdr)); hdr->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); hdr->rpc_vers = cpu_to_be32(2); hdr->prog = cpu_to_be32(prog); hdr->vers = cpu_to_be32(vers); hdr->procedure = cpu_to_be32(proc); } EXPORT_SYMBOL(msm_rpc_setup_req); struct msm_rpc_endpoint *msm_rpc_open(void) { struct msm_rpc_endpoint *ept; ept = msm_rpcrouter_create_local_endpoint(MKDEV(0, 0)); if (ept == NULL) return ERR_PTR(-ENOMEM); return ept; } void msm_rpc_read_wakeup(struct msm_rpc_endpoint *ept) { ept->forced_wakeup = 1; wake_up(&ept->wait_q); } int msm_rpc_close(struct msm_rpc_endpoint *ept) { if (!ept) return -EINVAL; return msm_rpcrouter_destroy_local_endpoint(ept); } EXPORT_SYMBOL(msm_rpc_close); static int msm_rpc_write_pkt( struct rr_header *hdr, struct msm_rpc_endpoint *ept, struct rr_remote_endpoint *r_ept, void *buffer, int count, int first, int last, uint32_t mid ) { #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) struct rpc_request_hdr *rq = buffer; uint32_t event_id; #endif uint32_t pacmark; unsigned long flags = 0; int rc; struct rpcrouter_xprt_info *xprt_info; int needed; DEFINE_WAIT(__wait); /* Create routing header */ hdr->type = RPCROUTER_CTRL_CMD_DATA; hdr->version = RPCROUTER_VERSION; hdr->src_pid = ept->pid; hdr->src_cid = ept->cid; hdr->confirm_rx = 0; hdr->size = count + sizeof(uint32_t); rc = wait_for_restart_and_notify(ept); if (rc) return rc; if (r_ept) { for (;;) { prepare_to_wait(&r_ept->quota_wait, &__wait, TASK_INTERRUPTIBLE); spin_lock_irqsave(&r_ept->quota_lock, flags); if ((r_ept->tx_quota_cntr < RPCROUTER_DEFAULT_RX_QUOTA) || (r_ept->quota_restart_state != RESTART_NORMAL)) break; if (signal_pending(current) && (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) break; spin_unlock_irqrestore(&r_ept->quota_lock, flags); schedule(); } finish_wait(&r_ept->quota_wait, &__wait); if (r_ept->quota_restart_state != RESTART_NORMAL) { spin_lock(&ept->restart_lock); ept->restart_state &= ~RESTART_PEND_NTFY; spin_unlock(&ept->restart_lock); spin_unlock_irqrestore(&r_ept->quota_lock, flags); return -ENETRESET; } if (signal_pending(current) && (!(ept->flags & MSM_RPC_UNINTERRUPTIBLE))) { spin_unlock_irqrestore(&r_ept->quota_lock, flags); return -ERESTARTSYS; } r_ept->tx_quota_cntr++; if (r_ept->tx_quota_cntr == RPCROUTER_DEFAULT_RX_QUOTA) { hdr->confirm_rx = 1; #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) if (smd_rpcrouter_debug_mask & SMEM_LOG) { event_id = (rq->xid == 0) ? RPC_ROUTER_LOG_EVENT_MID_CFM_REQ : RPC_ROUTER_LOG_EVENT_MSG_CFM_REQ; smem_log_event(SMEM_LOG_PROC_ID_APPS | event_id, hdr->dst_pid, hdr->dst_cid, hdr->src_cid); } #endif } } pacmark = PACMARK(count, mid, first, last); if (r_ept) spin_unlock_irqrestore(&r_ept->quota_lock, flags); mutex_lock(&xprt_info_list_lock); xprt_info = rpcrouter_get_xprt_info(hdr->dst_pid); if (!xprt_info) { mutex_unlock(&xprt_info_list_lock); return -ENETRESET; } spin_lock_irqsave(&xprt_info->lock, flags); mutex_unlock(&xprt_info_list_lock); spin_lock(&ept->restart_lock); if (ept->restart_state != RESTART_NORMAL) { ept->restart_state &= ~RESTART_PEND_NTFY; spin_unlock(&ept->restart_lock); spin_unlock_irqrestore(&xprt_info->lock, flags); return -ENETRESET; } needed = sizeof(*hdr) + hdr->size; while ((ept->restart_state == RESTART_NORMAL) && (xprt_info->xprt->write_avail() < needed)) { spin_unlock(&ept->restart_lock); spin_unlock_irqrestore(&xprt_info->lock, flags); msleep(250); /* refresh xprt pointer to ensure that it hasn't * been deleted since our last retrieval */ mutex_lock(&xprt_info_list_lock); xprt_info = rpcrouter_get_xprt_info(hdr->dst_pid); if (!xprt_info) { mutex_unlock(&xprt_info_list_lock); return -ENETRESET; } spin_lock_irqsave(&xprt_info->lock, flags); mutex_unlock(&xprt_info_list_lock); spin_lock(&ept->restart_lock); } if (ept->restart_state != RESTART_NORMAL) { ept->restart_state &= ~RESTART_PEND_NTFY; spin_unlock(&ept->restart_lock); spin_unlock_irqrestore(&xprt_info->lock, flags); return -ENETRESET; } /* TODO: deal with full fifo */ xprt_info->xprt->write(hdr, sizeof(*hdr), HEADER); RAW_HDR("[w rr_h] " "ver=%i,type=%s,src_pid=%08x,src_cid=%08x," "confirm_rx=%i,size=%3i,dst_pid=%08x,dst_cid=%08x\n", hdr->version, type_to_str(hdr->type), hdr->src_pid, hdr->src_cid, hdr->confirm_rx, hdr->size, hdr->dst_pid, hdr->dst_cid); xprt_info->xprt->write(&pacmark, sizeof(pacmark), PACKMARK); #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) if ((smd_rpcrouter_debug_mask & RAW_PMW) && ((pacmark >> 30 & 0x1) || (pacmark >> 31 & 0x1))) { uint32_t xid = 0; if (pacmark >> 30 & 0x1) xid = ntohl(rq->xid); if ((pacmark >> 31 & 0x1) || (pacmark >> 30 & 0x1)) RAW_PMW_NOMASK("xid:0x%03x first=%i,last=%i,mid=%3i," "len=%3i,src_cid=%x\n", xid, pacmark >> 30 & 0x1, pacmark >> 31 & 0x1, pacmark >> 16 & 0xFF, pacmark & 0xFFFF, hdr->src_cid); } #endif xprt_info->xprt->write(buffer, count, PAYLOAD); spin_unlock(&ept->restart_lock); spin_unlock_irqrestore(&xprt_info->lock, flags); #if defined(CONFIG_MSM_ONCRPCROUTER_DEBUG) if (smd_rpcrouter_debug_mask & SMEM_LOG) { if (rq->xid == 0) smem_log_event(SMEM_LOG_PROC_ID_APPS | RPC_ROUTER_LOG_EVENT_MID_WRITTEN, PACMARK_MID(pacmark), hdr->dst_cid, hdr->src_cid); else smem_log_event(SMEM_LOG_PROC_ID_APPS | RPC_ROUTER_LOG_EVENT_MSG_WRITTEN, ntohl(rq->xid), hdr->dst_cid, hdr->src_cid); } #endif return needed; } static struct msm_rpc_reply *get_pend_reply(struct msm_rpc_endpoint *ept, uint32_t xid) { unsigned long flags; struct msm_rpc_reply *reply; spin_lock_irqsave(&ept->reply_q_lock, flags); list_for_each_entry(reply, &ept->reply_pend_q, list) { if (reply->xid == xid) { list_del(&reply->list); spin_unlock_irqrestore(&ept->reply_q_lock, flags); return reply; } } spin_unlock_irqrestore(&ept->reply_q_lock, flags); return NULL; } void get_requesting_client(struct msm_rpc_endpoint *ept, uint32_t xid, struct msm_rpc_client_info *clnt_info) { unsigned long flags; struct msm_rpc_reply *reply; if (!clnt_info) return; spin_lock_irqsave(&ept->reply_q_lock, flags); list_for_each_entry(reply, &ept->reply_pend_q, list) { if (reply->xid == xid) { clnt_info->pid = reply->pid; clnt_info->cid = reply->cid; clnt_info->prog = reply->prog; clnt_info->vers = reply->vers; spin_unlock_irqrestore(&ept->reply_q_lock, flags); return; } } spin_unlock_irqrestore(&ept->reply_q_lock, flags); return; } static void set_avail_reply(struct msm_rpc_endpoint *ept, struct msm_rpc_reply *reply) { unsigned long flags; spin_lock_irqsave(&ept->reply_q_lock, flags); list_add_tail(&reply->list, &ept->reply_avail_q); spin_unlock_irqrestore(&ept->reply_q_lock, flags); } static struct msm_rpc_reply *get_avail_reply(struct msm_rpc_endpoint *ept) { struct msm_rpc_reply *reply; unsigned long flags; if (list_empty(&ept->reply_avail_q)) { if (ept->reply_cnt >= RPCROUTER_PEND_REPLIES_MAX) { printk(KERN_ERR "exceeding max replies of %d \n", RPCROUTER_PEND_REPLIES_MAX); return 0; } reply = kmalloc(sizeof(struct msm_rpc_reply), GFP_KERNEL); if (!reply) return 0; D("Adding reply 0x%08x \n", (unsigned int)reply); memset(reply, 0, sizeof(struct msm_rpc_reply)); spin_lock_irqsave(&ept->reply_q_lock, flags); ept->reply_cnt++; spin_unlock_irqrestore(&ept->reply_q_lock, flags); } else { spin_lock_irqsave(&ept->reply_q_lock, flags); reply = list_first_entry(&ept->reply_avail_q, struct msm_rpc_reply, list); list_del(&reply->list); spin_unlock_irqrestore(&ept->reply_q_lock, flags); } return reply; } static void set_pend_reply(struct msm_rpc_endpoint *ept, struct msm_rpc_reply *reply) { unsigned long flags; spin_lock_irqsave(&ept->reply_q_lock, flags); D("%s: take reply lock on ept %p\n", __func__, ept); wake_lock(&ept->reply_q_wake_lock); list_add_tail(&reply->list, &ept->reply_pend_q); spin_unlock_irqrestore(&ept->reply_q_lock, flags); } int msm_rpc_write(struct msm_rpc_endpoint *ept, void *buffer, int count) { struct rr_header hdr; struct rpc_request_hdr *rq = buffer; struct rr_remote_endpoint *r_ept; struct msm_rpc_reply *reply = NULL; int max_tx; int tx_cnt; char *tx_buf; int rc; int first_pkt = 1; uint32_t mid; unsigned long flags; /* snoop the RPC packet and enforce permissions */ /* has to have at least the xid and type fields */ if (count < (sizeof(uint32_t) * 2)) { printk(KERN_ERR "rr_write: rejecting runt packet\n"); return -EINVAL; } if (rq->type == 0) { /* RPC CALL */ if (count < (sizeof(uint32_t) * 6)) { printk(KERN_ERR "rr_write: rejecting runt call packet\n"); return -EINVAL; } if (ept->dst_pid == 0xffffffff) { printk(KERN_ERR "rr_write: not connected\n"); return -ENOTCONN; } if ((ept->dst_prog != rq->prog) || ((be32_to_cpu(ept->dst_vers) & 0x0fff0000) != (be32_to_cpu(rq->vers) & 0x0fff0000))) { printk(KERN_ERR "rr_write: cannot write to %08x:%08x " "(bound to %08x:%08x)\n", be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), be32_to_cpu(ept->dst_prog), be32_to_cpu(ept->dst_vers)); return -EINVAL; } hdr.dst_pid = ept->dst_pid; hdr.dst_cid = ept->dst_cid; IO("CALL to %08x:%d @ %d:%08x (%d bytes)\n", be32_to_cpu(rq->prog), be32_to_cpu(rq->vers), ept->dst_pid, ept->dst_cid, count); } else { /* RPC REPLY */ reply = get_pend_reply(ept, rq->xid); if (!reply) { printk(KERN_ERR "rr_write: rejecting, reply not found \n"); return -EINVAL; } hdr.dst_pid = reply->pid; hdr.dst_cid = reply->cid; IO("REPLY to xid=%d @ %d:%08x (%d bytes)\n", be32_to_cpu(rq->xid), hdr.dst_pid, hdr.dst_cid, count); } r_ept = rpcrouter_lookup_remote_endpoint(hdr.dst_pid, hdr.dst_cid); if ((!r_ept) && (hdr.dst_pid != RPCROUTER_PID_LOCAL)) { printk(KERN_ERR "msm_rpc_write(): No route to ept " "[PID %x CID %x]\n", hdr.dst_pid, hdr.dst_cid); count = -EHOSTUNREACH; goto write_release_lock; } tx_cnt = count; tx_buf = buffer; mid = atomic_add_return(1, &pm_mid) & 0xFF; /* The modem's router can only take 500 bytes of data. The first 8 bytes it uses on the modem side for addressing, the next 4 bytes are for the pacmark header. */ max_tx = RPCROUTER_MSGSIZE_MAX - 8 - sizeof(uint32_t); IO("Writing %d bytes, max pkt size is %d\n", tx_cnt, max_tx); while (tx_cnt > 0) { if (tx_cnt > max_tx) { rc = msm_rpc_write_pkt(&hdr, ept, r_ept, tx_buf, max_tx, first_pkt, 0, mid); if (rc < 0) { count = rc; goto write_release_lock; } IO("Wrote %d bytes First %d, Last 0 mid %d\n", rc, first_pkt, mid); tx_cnt -= max_tx; tx_buf += max_tx; } else { rc = msm_rpc_write_pkt(&hdr, ept, r_ept, tx_buf, tx_cnt, first_pkt, 1, mid); if (rc < 0) { count = rc; goto write_release_lock; } IO("Wrote %d bytes First %d Last 1 mid %d\n", rc, first_pkt, mid); break; } first_pkt = 0; } write_release_lock: /* if reply, release wakelock after writing to the transport */ if (rq->type != 0) { /* Upon failure, add reply tag to the pending list. ** Else add reply tag to the avail/free list. */ if (count < 0) set_pend_reply(ept, reply); else set_avail_reply(ept, reply); spin_lock_irqsave(&ept->reply_q_lock, flags); if (list_empty(&ept->reply_pend_q)) { D("%s: release reply lock on ept %p\n", __func__, ept); wake_unlock(&ept->reply_q_wake_lock); } spin_unlock_irqrestore(&ept->reply_q_lock, flags); } return count; } EXPORT_SYMBOL(msm_rpc_write); /* * NOTE: It is the responsibility of the caller to kfree buffer */ int msm_rpc_read(struct msm_rpc_endpoint *ept, void **buffer, unsigned user_len, long timeout) { struct rr_fragment *frag, *next; char *buf; int rc; rc = __msm_rpc_read(ept, &frag, user_len, timeout); if (rc <= 0) return rc; /* single-fragment messages conveniently can be * returned as-is (the buffer is at the front) */ if (frag->next == 0) { *buffer = (void*) frag; return rc; } /* multi-fragment messages, we have to do it the * hard way, which is rather disgusting right now */ buf = rr_malloc(rc); *buffer = buf; while (frag != NULL) { memcpy(buf, frag->data, frag->length); next = frag->next; buf += frag->length; kfree(frag); frag = next; } return rc; } EXPORT_SYMBOL(msm_rpc_read); int msm_rpc_call(struct msm_rpc_endpoint *ept, uint32_t proc, void *_request, int request_size, long timeout) { return msm_rpc_call_reply(ept, proc, _request, request_size, NULL, 0, timeout); } EXPORT_SYMBOL(msm_rpc_call); int msm_rpc_call_reply(struct msm_rpc_endpoint *ept, uint32_t proc, void *_request, int request_size, void *_reply, int reply_size, long timeout) { struct rpc_request_hdr *req = _request; struct rpc_reply_hdr *reply; int rc; if (request_size < sizeof(*req)) return -ETOOSMALL; if (ept->dst_pid == 0xffffffff) return -ENOTCONN; memset(req, 0, sizeof(*req)); req->xid = cpu_to_be32(atomic_add_return(1, &next_xid)); req->rpc_vers = cpu_to_be32(2); req->prog = ept->dst_prog; req->vers = ept->dst_vers; req->procedure = cpu_to_be32(proc); rc = msm_rpc_write(ept, req, request_size); if (rc < 0) return rc; for (;;) { rc = msm_rpc_read(ept, (void*) &reply, -1, timeout); if (rc < 0) return rc; if (rc < (3 * sizeof(uint32_t))) { rc = -EIO; break; } /* we should not get CALL packets -- ignore them */ if (reply->type == 0) { kfree(reply); continue; } /* If an earlier call timed out, we could get the (no * longer wanted) reply for it. Ignore replies that * we don't expect */ if (reply->xid != req->xid) { kfree(reply); continue; } if (reply->reply_stat != 0) { rc = -EPERM; break; } if (reply->data.acc_hdr.accept_stat != 0) { rc = -EINVAL; break; } if (_reply == NULL) { rc = 0; break; } if (rc > reply_size) { rc = -ENOMEM; } else { memcpy(_reply, reply, rc); } break; } kfree(reply); return rc; } EXPORT_SYMBOL(msm_rpc_call_reply); static inline int ept_packet_available(struct msm_rpc_endpoint *ept) { unsigned long flags; int ret; spin_lock_irqsave(&ept->read_q_lock, flags); ret = !list_empty(&ept->read_q); spin_unlock_irqrestore(&ept->read_q_lock, flags); return ret; } int __msm_rpc_read(struct msm_rpc_endpoint *ept, struct rr_fragment **frag_ret, unsigned len, long timeout) { struct rr_packet *pkt; struct rpc_request_hdr *rq; struct msm_rpc_reply *reply; unsigned long flags; int rc; rc = wait_for_restart_and_notify(ept); if (rc) return rc; IO("READ on ept %p\n", ept); if (ept->flags & MSM_RPC_UNINTERRUPTIBLE) { if (timeout < 0) { wait_event(ept->wait_q, (ept_packet_available(ept) || ept->forced_wakeup || ept->restart_state)); if (!msm_rpc_clear_netreset(ept)) return -ENETRESET; } else { rc = wait_event_timeout( ept->wait_q, (ept_packet_available(ept) || ept->forced_wakeup || ept->restart_state), timeout); if (!msm_rpc_clear_netreset(ept)) return -ENETRESET; if (rc == 0) return -ETIMEDOUT; } } else { if (timeout < 0) { rc = wait_event_interruptible( ept->wait_q, (ept_packet_available(ept) || ept->forced_wakeup || ept->restart_state)); if (!msm_rpc_clear_netreset(ept)) return -ENETRESET; if (rc < 0) return rc; } else { rc = wait_event_interruptible_timeout( ept->wait_q, (ept_packet_available(ept) || ept->forced_wakeup || ept->restart_state), timeout); if (!msm_rpc_clear_netreset(ept)) return -ENETRESET; if (rc == 0) return -ETIMEDOUT; } } if (ept->forced_wakeup) { ept->forced_wakeup = 0; return 0; } spin_lock_irqsave(&ept->read_q_lock, flags); if (list_empty(&ept->read_q)) { spin_unlock_irqrestore(&ept->read_q_lock, flags); return -EAGAIN; } pkt = list_first_entry(&ept->read_q, struct rr_packet, list); if (pkt->length > len) { spin_unlock_irqrestore(&ept->read_q_lock, flags); return -ETOOSMALL; } list_del(&pkt->list); spin_unlock_irqrestore(&ept->read_q_lock, flags); rc = pkt->length; *frag_ret = pkt->first; rq = (void*) pkt->first->data; if ((rc >= (sizeof(uint32_t) * 3)) && (rq->type == 0)) { /* RPC CALL */ reply = get_avail_reply(ept); if (!reply) { rc = -ENOMEM; goto read_release_lock; } reply->cid = pkt->hdr.src_cid; reply->pid = pkt->hdr.src_pid; reply->xid = rq->xid; reply->prog = rq->prog; reply->vers = rq->vers; set_pend_reply(ept, reply); } kfree(pkt); IO("READ on ept %p (%d bytes)\n", ept, rc); read_release_lock: /* release read wakelock after taking reply wakelock */ spin_lock_irqsave(&ept->read_q_lock, flags); if (list_empty(&ept->read_q)) { D("%s: release read lock on ept %p\n", __func__, ept); wake_unlock(&ept->read_q_wake_lock); } spin_unlock_irqrestore(&ept->read_q_lock, flags); return rc; } int msm_rpc_is_compatible_version(uint32_t server_version, uint32_t client_version) { if ((server_version & RPC_VERSION_MODE_MASK) != (client_version & RPC_VERSION_MODE_MASK)) return 0; if (server_version & RPC_VERSION_MODE_MASK) return server_version == client_version; return ((server_version & RPC_VERSION_MAJOR_MASK) == (client_version & RPC_VERSION_MAJOR_MASK)) && ((server_version & RPC_VERSION_MINOR_MASK) >= (client_version & RPC_VERSION_MINOR_MASK)); } EXPORT_SYMBOL(msm_rpc_is_compatible_version); static struct rr_server *msm_rpc_get_server(uint32_t prog, uint32_t vers, uint32_t accept_compatible, uint32_t *found_prog) { struct rr_server *server; unsigned long flags; if (found_prog == NULL) return NULL; *found_prog = 0; spin_lock_irqsave(&server_list_lock, flags); list_for_each_entry(server, &server_list, list) { if (server->prog == prog) { *found_prog = 1; spin_unlock_irqrestore(&server_list_lock, flags); if (accept_compatible) { if (msm_rpc_is_compatible_version(server->vers, vers)) { return server; } else { return NULL; } } else if (server->vers == vers) { return server; } else return NULL; } } spin_unlock_irqrestore(&server_list_lock, flags); return NULL; } static struct msm_rpc_endpoint *__msm_rpc_connect(uint32_t prog, uint32_t vers, uint32_t accept_compatible, unsigned flags) { struct msm_rpc_endpoint *ept; struct rr_server *server; uint32_t found_prog; int rc = 0; DEFINE_WAIT(__wait); for (;;) { prepare_to_wait(&newserver_wait, &__wait, TASK_INTERRUPTIBLE); server = msm_rpc_get_server(prog, vers, accept_compatible, &found_prog); if (server) break; if (found_prog) { pr_info("%s: server not found %x:%x\n", __func__, prog, vers); rc = -EHOSTUNREACH; break; } if (msm_rpc_connect_timeout_ms == 0) { rc = -EHOSTUNREACH; break; } if (signal_pending(current)) { rc = -ERESTARTSYS; break; } rc = schedule_timeout( msecs_to_jiffies(msm_rpc_connect_timeout_ms)); if (!rc) { rc = -ETIMEDOUT; break; } } finish_wait(&newserver_wait, &__wait); if (!server) return ERR_PTR(rc); if (accept_compatible && (server->vers != vers)) { D("RPC Using new version 0x%08x(0x%08x) prog 0x%08x", vers, server->vers, prog); D(" ... Continuing\n"); } ept = msm_rpc_open(); if (IS_ERR(ept)) return ept; ept->flags = flags; ept->dst_pid = server->pid; ept->dst_cid = server->cid; ept->dst_prog = cpu_to_be32(prog); ept->dst_vers = cpu_to_be32(server->vers); return ept; } struct msm_rpc_endpoint *msm_rpc_connect_compatible(uint32_t prog, uint32_t vers, unsigned flags) { return __msm_rpc_connect(prog, vers, 1, flags); } EXPORT_SYMBOL(msm_rpc_connect_compatible); struct msm_rpc_endpoint *msm_rpc_connect(uint32_t prog, uint32_t vers, unsigned flags) { return __msm_rpc_connect(prog, vers, 0, flags); } EXPORT_SYMBOL(msm_rpc_connect); /* TODO: permission check? */ int msm_rpc_register_server(struct msm_rpc_endpoint *ept, uint32_t prog, uint32_t vers) { int rc; union rr_control_msg msg; struct rr_server *server; struct rpcrouter_xprt_info *xprt_info; server = rpcrouter_create_server(ept->pid, ept->cid, prog, vers); if (!server) return -ENODEV; msg.srv.cmd = RPCROUTER_CTRL_CMD_NEW_SERVER; msg.srv.pid = ept->pid; msg.srv.cid = ept->cid; msg.srv.prog = prog; msg.srv.vers = vers; RR("x NEW_SERVER id=%d:%08x prog=%08x:%08x\n", ept->pid, ept->cid, prog, vers); mutex_lock(&xprt_info_list_lock); list_for_each_entry(xprt_info, &xprt_info_list, list) { rc = rpcrouter_send_control_msg(xprt_info, &msg); if (rc < 0) { mutex_unlock(&xprt_info_list_lock); return rc; } } mutex_unlock(&xprt_info_list_lock); return 0; } int msm_rpc_clear_netreset(struct msm_rpc_endpoint *ept) { unsigned long flags; int rc = 1; spin_lock_irqsave(&ept->restart_lock, flags); if (ept->restart_state != RESTART_NORMAL) { ept->restart_state &= ~RESTART_PEND_NTFY; rc = 0; } spin_unlock_irqrestore(&ept->restart_lock, flags); return rc; } /* TODO: permission check -- disallow unreg of somebody else's server */ int msm_rpc_unregister_server(struct msm_rpc_endpoint *ept, uint32_t prog, uint32_t vers) { struct rr_server *server; server = rpcrouter_lookup_server(prog, vers); if (!server) return -ENOENT; rpcrouter_destroy_server(server); return 0; } int msm_rpc_get_curr_pkt_size(struct msm_rpc_endpoint *ept) { unsigned long flags; struct rr_packet *pkt; int rc = 0; if (!ept) return -EINVAL; if (!msm_rpc_clear_netreset(ept)) return -ENETRESET; spin_lock_irqsave(&ept->read_q_lock, flags); if (!list_empty(&ept->read_q)) { pkt = list_first_entry(&ept->read_q, struct rr_packet, list); rc = pkt->length; } spin_unlock_irqrestore(&ept->read_q_lock, flags); return rc; } int msm_rpcrouter_close(void) { struct rpcrouter_xprt_info *xprt_info; union rr_control_msg ctl; ctl.cmd = RPCROUTER_CTRL_CMD_BYE; mutex_lock(&xprt_info_list_lock); while (!list_empty(&xprt_info_list)) { xprt_info = list_first_entry(&xprt_info_list, struct rpcrouter_xprt_info, list); xprt_info->abort_data_read = 1; wake_up(&xprt_info->read_wait); rpcrouter_send_control_msg(xprt_info, &ctl); xprt_info->xprt->close(); list_del(&xprt_info->list); mutex_unlock(&xprt_info_list_lock); flush_workqueue(xprt_info->workqueue); destroy_workqueue(xprt_info->workqueue); wake_lock_destroy(&xprt_info->wakelock); kfree(xprt_info); mutex_lock(&xprt_info_list_lock); } mutex_unlock(&xprt_info_list_lock); return 0; } #if defined(CONFIG_DEBUG_FS) static int dump_servers(char *buf, int max) { int i = 0; unsigned long flags; struct rr_server *svr; const char *sym; spin_lock_irqsave(&server_list_lock, flags); list_for_each_entry(svr, &server_list, list) { i += scnprintf(buf + i, max - i, "pdev_name: %s\n", svr->pdev_name); i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", svr->pid); i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", svr->cid); i += scnprintf(buf + i, max - i, "prog: 0x%08x", svr->prog); sym = smd_rpc_get_sym(svr->prog); if (sym) i += scnprintf(buf + i, max - i, " (%s)\n", sym); else i += scnprintf(buf + i, max - i, "\n"); i += scnprintf(buf + i, max - i, "vers: 0x%08x\n", svr->vers); i += scnprintf(buf + i, max - i, "\n"); } spin_unlock_irqrestore(&server_list_lock, flags); return i; } static int dump_remote_endpoints(char *buf, int max) { int i = 0; unsigned long flags; struct rr_remote_endpoint *ept; spin_lock_irqsave(&remote_endpoints_lock, flags); list_for_each_entry(ept, &remote_endpoints, list) { i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid); i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid); i += scnprintf(buf + i, max - i, "tx_quota_cntr: %i\n", ept->tx_quota_cntr); i += scnprintf(buf + i, max - i, "quota_restart_state: %i\n", ept->quota_restart_state); i += scnprintf(buf + i, max - i, "\n"); } spin_unlock_irqrestore(&remote_endpoints_lock, flags); return i; } static int dump_msm_rpc_endpoint(char *buf, int max) { int i = 0; unsigned long flags; struct msm_rpc_reply *reply; struct msm_rpc_endpoint *ept; struct rr_packet *pkt; const char *sym; spin_lock_irqsave(&local_endpoints_lock, flags); list_for_each_entry(ept, &local_endpoints, list) { i += scnprintf(buf + i, max - i, "pid: 0x%08x\n", ept->pid); i += scnprintf(buf + i, max - i, "cid: 0x%08x\n", ept->cid); i += scnprintf(buf + i, max - i, "dst_pid: 0x%08x\n", ept->dst_pid); i += scnprintf(buf + i, max - i, "dst_cid: 0x%08x\n", ept->dst_cid); i += scnprintf(buf + i, max - i, "dst_prog: 0x%08x", be32_to_cpu(ept->dst_prog)); sym = smd_rpc_get_sym(be32_to_cpu(ept->dst_prog)); if (sym) i += scnprintf(buf + i, max - i, " (%s)\n", sym); else i += scnprintf(buf + i, max - i, "\n"); i += scnprintf(buf + i, max - i, "dst_vers: 0x%08x\n", be32_to_cpu(ept->dst_vers)); i += scnprintf(buf + i, max - i, "reply_cnt: %i\n", ept->reply_cnt); i += scnprintf(buf + i, max - i, "restart_state: %i\n", ept->restart_state); i += scnprintf(buf + i, max - i, "outstanding xids:\n"); spin_lock(&ept->reply_q_lock); list_for_each_entry(reply, &ept->reply_pend_q, list) i += scnprintf(buf + i, max - i, " xid = %u\n", ntohl(reply->xid)); spin_unlock(&ept->reply_q_lock); i += scnprintf(buf + i, max - i, "complete unread packets:\n"); spin_lock(&ept->read_q_lock); list_for_each_entry(pkt, &ept->read_q, list) { i += scnprintf(buf + i, max - i, " mid = %i\n", pkt->mid); i += scnprintf(buf + i, max - i, " length = %i\n", pkt->length); } spin_unlock(&ept->read_q_lock); i += scnprintf(buf + i, max - i, "\n"); } spin_unlock_irqrestore(&local_endpoints_lock, flags); return i; } #define DEBUG_BUFMAX 4096 static char debug_buffer[DEBUG_BUFMAX]; static ssize_t debug_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int (*fill)(char *buf, int max) = file->private_data; int bsize = fill(debug_buffer, DEBUG_BUFMAX); return simple_read_from_buffer(buf, count, ppos, debug_buffer, bsize); } static int debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; return 0; } static const struct file_operations debug_ops = { .read = debug_read, .open = debug_open, }; static void debug_create(const char *name, mode_t mode, struct dentry *dent, int (*fill)(char *buf, int max)) { debugfs_create_file(name, mode, dent, fill, &debug_ops); } static void debugfs_init(void) { struct dentry *dent; dent = debugfs_create_dir("smd_rpcrouter", 0); if (IS_ERR(dent)) return; debug_create("dump_msm_rpc_endpoints", 0444, dent, dump_msm_rpc_endpoint); debug_create("dump_remote_endpoints", 0444, dent, dump_remote_endpoints); debug_create("dump_servers", 0444, dent, dump_servers); } #else static void debugfs_init(void) {} #endif static int msm_rpcrouter_add_xprt(struct rpcrouter_xprt *xprt) { struct rpcrouter_xprt_info *xprt_info; D("Registering xprt %s to RPC Router\n", xprt->name); xprt_info = kmalloc(sizeof(struct rpcrouter_xprt_info), GFP_KERNEL); if (!xprt_info) return -ENOMEM; xprt_info->xprt = xprt; xprt_info->initialized = 0; xprt_info->remote_pid = -1; init_waitqueue_head(&xprt_info->read_wait); spin_lock_init(&xprt_info->lock); wake_lock_init(&xprt_info->wakelock, WAKE_LOCK_SUSPEND, xprt->name); xprt_info->need_len = 0; xprt_info->abort_data_read = 0; INIT_WORK(&xprt_info->read_data, do_read_data); INIT_LIST_HEAD(&xprt_info->list); xprt_info->workqueue = create_singlethread_workqueue(xprt->name); if (!xprt_info->workqueue) { kfree(xprt_info); return -ENOMEM; } if (!strcmp(xprt->name, "rpcrouter_loopback_xprt")) { xprt_info->remote_pid = RPCROUTER_PID_LOCAL; xprt_info->initialized = 1; } else { smsm_change_state(SMSM_APPS_STATE, 0, SMSM_RPCINIT); } mutex_lock(&xprt_info_list_lock); list_add_tail(&xprt_info->list, &xprt_info_list); mutex_unlock(&xprt_info_list_lock); queue_work(xprt_info->workqueue, &xprt_info->read_data); xprt->priv = xprt_info; return 0; } static void msm_rpcrouter_remove_xprt(struct rpcrouter_xprt *xprt) { struct rpcrouter_xprt_info *xprt_info; unsigned long flags; if (xprt && xprt->priv) { xprt_info = xprt->priv; /* abort rr_read thread */ xprt_info->abort_data_read = 1; wake_up(&xprt_info->read_wait); /* remove xprt from available xprts */ mutex_lock(&xprt_info_list_lock); spin_lock_irqsave(&xprt_info->lock, flags); list_del(&xprt_info->list); /* unlock the spinlock last to avoid a race * condition with rpcrouter_get_xprt_info * in msm_rpc_write_pkt in which the * xprt is returned from rpcrouter_get_xprt_info * and then deleted here. */ mutex_unlock(&xprt_info_list_lock); spin_unlock_irqrestore(&xprt_info->lock, flags); /* cleanup workqueues and wakelocks */ flush_workqueue(xprt_info->workqueue); destroy_workqueue(xprt_info->workqueue); wake_lock_destroy(&xprt_info->wakelock); /* free memory */ xprt->priv = 0; kfree(xprt_info); } } struct rpcrouter_xprt_work { struct rpcrouter_xprt *xprt; struct work_struct work; }; static void xprt_open_worker(struct work_struct *work) { struct rpcrouter_xprt_work *xprt_work = container_of(work, struct rpcrouter_xprt_work, work); msm_rpcrouter_add_xprt(xprt_work->xprt); kfree(xprt_work); } static void xprt_close_worker(struct work_struct *work) { struct rpcrouter_xprt_work *xprt_work = container_of(work, struct rpcrouter_xprt_work, work); modem_reset_cleanup(xprt_work->xprt->priv); msm_rpcrouter_remove_xprt(xprt_work->xprt); if (atomic_dec_return(&pending_close_count) == 0) wake_up(&subsystem_restart_wait); kfree(xprt_work); } void msm_rpcrouter_xprt_notify(struct rpcrouter_xprt *xprt, unsigned event) { struct rpcrouter_xprt_info *xprt_info; struct rpcrouter_xprt_work *xprt_work; /* Workqueue is created in init function which works for all existing * clients. If this fails in the future, then it will need to be * created earlier. */ BUG_ON(!rpcrouter_workqueue); switch (event) { case RPCROUTER_XPRT_EVENT_OPEN: D("open event for '%s'\n", xprt->name); xprt_work = kmalloc(sizeof(struct rpcrouter_xprt_work), GFP_ATOMIC); xprt_work->xprt = xprt; INIT_WORK(&xprt_work->work, xprt_open_worker); queue_work(rpcrouter_workqueue, &xprt_work->work); break; case RPCROUTER_XPRT_EVENT_CLOSE: D("close event for '%s'\n", xprt->name); atomic_inc(&pending_close_count); xprt_work = kmalloc(sizeof(struct rpcrouter_xprt_work), GFP_ATOMIC); xprt_work->xprt = xprt; INIT_WORK(&xprt_work->work, xprt_close_worker); queue_work(rpcrouter_workqueue, &xprt_work->work); break; } xprt_info = xprt->priv; if (xprt_info) { /* Check read_avail even for OPEN event to handle missed DATA events while processing the OPEN event*/ if (xprt->read_avail() >= xprt_info->need_len) wake_lock(&xprt_info->wakelock); wake_up(&xprt_info->read_wait); } } static int modem_restart_notifier_cb(struct notifier_block *this, unsigned long code, void *data); static struct notifier_block nb = { .notifier_call = modem_restart_notifier_cb, }; static int modem_restart_notifier_cb(struct notifier_block *this, unsigned long code, void *data) { switch (code) { case SUBSYS_BEFORE_SHUTDOWN: D("%s: SUBSYS_BEFORE_SHUTDOWN\n", __func__); break; case SUBSYS_BEFORE_POWERUP: D("%s: waiting for RPC restart to complete\n", __func__); wait_event(subsystem_restart_wait, atomic_read(&pending_close_count) == 0); D("%s: finished restart wait\n", __func__); break; default: break; } return NOTIFY_DONE; } static void *restart_notifier_handle; static __init int modem_restart_late_init(void) { restart_notifier_handle = subsys_notif_register_notifier("modem", &nb); return 0; } late_initcall(modem_restart_late_init); static int __init rpcrouter_init(void) { int ret; msm_rpc_connect_timeout_ms = 0; smd_rpcrouter_debug_mask |= SMEM_LOG; debugfs_init(); /* Initialize what we need to start processing */ rpcrouter_workqueue = create_singlethread_workqueue("rpcrouter"); if (!rpcrouter_workqueue) { msm_rpcrouter_exit_devices(); return -ENOMEM; } init_waitqueue_head(&newserver_wait); init_waitqueue_head(&subsystem_restart_wait); ret = msm_rpcrouter_init_devices(); if (ret < 0) return ret; return ret; } module_init(rpcrouter_init); MODULE_DESCRIPTION("MSM RPC Router"); MODULE_AUTHOR("San Mehat "); MODULE_LICENSE("GPL");