blob: bdda54640748b8b5e1a064f1895784bbc14cf974 [file] [log] [blame]
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -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#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/net.h>
16#include <linux/socket.h>
17#include <linux/errno.h>
18#include <linux/mm.h>
19#include <linux/poll.h>
20#include <linux/fcntl.h>
21#include <linux/gfp.h>
22#include <linux/msm_ipc.h>
Zaheerulla Meerba10d372013-02-21 17:56:18 +053023#include <linux/sched.h>
24#include <linux/thread_info.h>
25#include <linux/qmi_encdec.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026
27#include <asm/string.h>
28#include <asm/atomic.h>
29
30#include <net/sock.h>
31
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -060032#include <mach/msm_ipc_router.h>
Zaheerulla Meerba10d372013-02-21 17:56:18 +053033#include <mach/msm_ipc_logging.h>
Karthikeyan Ramasubramanianefc493b2012-07-12 10:25:49 -060034
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#include "ipc_router.h"
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -060036#include "msm_ipc_router_security.h"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037
38#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
39#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
Zaheerulla Meerba10d372013-02-21 17:56:18 +053040#define REQ_RESP_IPC_LOG_PAGES 5
41#define IND_IPC_LOG_PAGES 5
42#define IPC_SEND 1
43#define IPC_RECV 2
44#define IPC_REQ_RESP_LOG(level, buf...) \
45do { \
46 if (ipc_req_resp_log_txt) { \
47 ipc_log_string(ipc_req_resp_log_txt, buf); \
48 } \
49} while (0) \
50
51#define IPC_IND_LOG(level, buf...) \
52do { \
53 if (ipc_ind_log_txt) { \
54 ipc_log_string(ipc_ind_log_txt, buf); \
55 } \
56} while (0) \
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057
Karthikeyan Ramasubramanian29dfa702013-05-31 15:36:38 -060058#ifndef SIZE_MAX
59#define SIZE_MAX ((size_t)-1)
60#endif
61
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062static int sockets_enabled;
63static struct proto msm_ipc_proto;
64static const struct proto_ops msm_ipc_proto_ops;
Zaheerulla Meerba10d372013-02-21 17:56:18 +053065static void *ipc_req_resp_log_txt;
66static void *ipc_ind_log_txt;
67
68/**
69 * msm_ipc_router_ipc_log() - Pass log data to IPC logging framework
70 * @tran: Identifies the data to be a receive or send.
71 * @ipc_buf: Buffer to extract the log data.
72 * @port_ptr: IPC Router port corresponding to the current log data.
73 *
74 * This function builds the data the would be passed on to the IPC logging
75 * framework. The data that would be passed corresponds to the information
76 * that is exchanged between the IPC Router and user space modules during
77 * request/response/indication transactions.
78 */
79
80static void msm_ipc_router_ipc_log(uint8_t tran,
81 struct sk_buff *ipc_buf, struct msm_ipc_port *port_ptr)
82{
83 struct qmi_header *hdr = (struct qmi_header *)ipc_buf->data;
84
85 /*
86 * IPC Logging format is as below:-
87 * <Name>(Name of the User Space Process):
88 * <PID> (PID of the user space process) :
89 * <TID> (TID of the user space thread) :
90 * <User Space Module>(CLNT or SERV) :
91 * <Opertaion Type> (Transmit) :
92 * <Control Flag> (Req/Resp/Ind) :
93 * <Transaction ID> :
94 * <Message ID> :
95 * <Message Length> :
96 */
97 if (ipc_req_resp_log_txt &&
98 (((uint8_t) hdr->cntl_flag == QMI_REQUEST_CONTROL_FLAG) ||
99 ((uint8_t) hdr->cntl_flag == QMI_RESPONSE_CONTROL_FLAG)) &&
100 (port_ptr->type == CLIENT_PORT ||
101 port_ptr->type == SERVER_PORT)) {
102 IPC_REQ_RESP_LOG(KERN_DEBUG,
103 "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
104 current->comm, current->tgid, current->pid,
105 (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
106 (tran == IPC_RECV ? "RX" :
107 (tran == IPC_SEND ? "TX" : "ERR")),
108 (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
109 hdr->msg_len);
110 } else if (ipc_ind_log_txt &&
111 ((uint8_t)hdr->cntl_flag == QMI_INDICATION_CONTROL_FLAG) &&
112 (port_ptr->type == CLIENT_PORT ||
113 port_ptr->type == SERVER_PORT)) {
114 IPC_IND_LOG(KERN_DEBUG,
115 "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
116 current->comm, current->tgid, current->pid,
117 (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
118 (tran == IPC_RECV ? "RX" :
119 (tran == IPC_SEND ? "TX" : "ERR")),
120 (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
121 hdr->msg_len);
122 }
123}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
126 struct iovec const *msg_sect,
127 size_t total_len)
128{
129 struct sk_buff_head *msg_head;
130 struct sk_buff *msg;
131 int i, copied, first = 1;
132 int data_size = 0, request_size, offset;
133 void *data;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600134 int last = 0;
135 int align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136
137 for (i = 0; i < num_sect; i++)
138 data_size += msg_sect[i].iov_len;
139
140 if (!data_size)
141 return NULL;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600142 align_size = ALIGN_SIZE(data_size);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700143
144 msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
145 if (!msg_head) {
146 pr_err("%s: cannot allocate skb_head\n", __func__);
147 return NULL;
148 }
149 skb_queue_head_init(msg_head);
150
151 for (copied = 1, i = 0; copied && (i < num_sect); i++) {
152 data_size = msg_sect[i].iov_len;
153 offset = 0;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600154 if (i == (num_sect - 1))
155 last = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 while (offset != msg_sect[i].iov_len) {
157 request_size = data_size;
158 if (first)
159 request_size += IPC_ROUTER_HDR_SIZE;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600160 if (last)
161 request_size += align_size;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162
163 msg = alloc_skb(request_size, GFP_KERNEL);
164 if (!msg) {
165 if (request_size <= (PAGE_SIZE/2)) {
166 pr_err("%s: cannot allocated skb\n",
167 __func__);
168 goto msg_build_failure;
169 }
170 data_size = data_size / 2;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600171 last = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 continue;
173 }
174
175 if (first) {
176 skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
177 first = 0;
178 }
179
180 data = skb_put(msg, data_size);
181 copied = !copy_from_user(msg->data,
182 msg_sect[i].iov_base + offset,
183 data_size);
184 if (!copied) {
185 pr_err("%s: copy_from_user failed\n",
186 __func__);
187 kfree_skb(msg);
188 goto msg_build_failure;
189 }
190 skb_queue_tail(msg_head, msg);
191 offset += data_size;
192 data_size = msg_sect[i].iov_len - offset;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600193 if (i == (num_sect - 1))
194 last = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 }
196 }
197 return msg_head;
198
199msg_build_failure:
200 while (!skb_queue_empty(msg_head)) {
201 msg = skb_dequeue(msg_head);
202 kfree_skb(msg);
203 }
204 kfree(msg_head);
205 return NULL;
206}
207
208static int msm_ipc_router_extract_msg(struct msghdr *m,
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600209 struct rr_packet *pkt)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210{
Karthikeyan Ramasubramanian4d1695b2013-04-05 11:30:53 -0600211 struct sockaddr_msm_ipc *addr;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600212 struct rr_header_v1 *hdr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 struct sk_buff *temp;
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530214 union rr_control_msg *ctl_msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700215 int offset = 0, data_len = 0, copy_len;
216
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600217 if (!m || !pkt) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218 pr_err("%s: Invalid pointers passed\n", __func__);
219 return -EINVAL;
220 }
Karthikeyan Ramasubramanian4d1695b2013-04-05 11:30:53 -0600221 addr = (struct sockaddr_msm_ipc *)m->msg_name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600223 hdr = &(pkt->hdr);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530224 if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600225 temp = skb_peek(pkt->pkt_fragment_q);
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530226 ctl_msg = (union rr_control_msg *)(temp->data);
227 addr->family = AF_MSM_IPC;
228 addr->address.addrtype = MSM_IPC_ADDR_ID;
229 addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id;
230 addr->address.addr.port_addr.port_id = ctl_msg->cli.port_id;
231 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
232 return offset;
233 }
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600234 if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_DATA)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 addr->family = AF_MSM_IPC;
236 addr->address.addrtype = MSM_IPC_ADDR_ID;
237 addr->address.addr.port_addr.node_id = hdr->src_node_id;
238 addr->address.addr.port_addr.port_id = hdr->src_port_id;
239 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
240 }
241
242 data_len = hdr->size;
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600243 skb_queue_walk(pkt->pkt_fragment_q, temp) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700244 copy_len = data_len < temp->len ? data_len : temp->len;
245 if (copy_to_user(m->msg_iov->iov_base + offset, temp->data,
246 copy_len)) {
247 pr_err("%s: Copy to user failed\n", __func__);
248 return -EFAULT;
249 }
250 offset += copy_len;
251 data_len -= copy_len;
252 }
253 return offset;
254}
255
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700256static int msm_ipc_router_create(struct net *net,
257 struct socket *sock,
258 int protocol,
259 int kern)
260{
261 struct sock *sk;
262 struct msm_ipc_port *port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263
264 if (unlikely(protocol != 0)) {
265 pr_err("%s: Protocol not supported\n", __func__);
266 return -EPROTONOSUPPORT;
267 }
268
269 switch (sock->type) {
270 case SOCK_DGRAM:
271 break;
272 default:
273 pr_err("%s: Protocol type not supported\n", __func__);
274 return -EPROTOTYPE;
275 }
276
277 sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto);
278 if (!sk) {
279 pr_err("%s: sk_alloc failed\n", __func__);
280 return -ENOMEM;
281 }
282
283 port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
284 if (!port_ptr) {
285 pr_err("%s: port_ptr alloc failed\n", __func__);
286 sk_free(sk);
287 return -ENOMEM;
288 }
289
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600290 port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291 sock->ops = &msm_ipc_proto_ops;
292 sock_init_data(sock, sk);
293 sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
294
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700295 msm_ipc_sk(sk)->port = port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296
297 return 0;
298}
299
300int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
301 int uaddr_len)
302{
303 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
304 struct sock *sk = sock->sk;
305 struct msm_ipc_port *port_ptr;
306 int ret;
Karthikeyan Ramasubramanian88f02e52013-06-03 12:05:50 -0600307 void *pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308
309 if (!sk)
310 return -EINVAL;
311
Karthikeyan Ramasubramanian13ef0552013-01-24 11:45:41 -0700312 if (!check_permissions()) {
313 pr_err("%s: %s Do not have permissions\n",
314 __func__, current->comm);
315 return -EPERM;
316 }
317
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318 if (!uaddr_len) {
319 pr_err("%s: Invalid address length\n", __func__);
320 return -EINVAL;
321 }
322
323 if (addr->family != AF_MSM_IPC) {
324 pr_err("%s: Address family is incorrect\n", __func__);
325 return -EAFNOSUPPORT;
326 }
327
328 if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
329 pr_err("%s: Address type is incorrect\n", __func__);
330 return -EINVAL;
331 }
332
333 port_ptr = msm_ipc_sk_port(sk);
334 if (!port_ptr)
335 return -ENODEV;
336
Karthikeyan Ramasubramanian88f02e52013-06-03 12:05:50 -0600337 pil = msm_ipc_load_default_node();
338 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700339 lock_sock(sk);
340
341 ret = msm_ipc_router_register_server(port_ptr, &addr->address);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342
343 release_sock(sk);
344 return ret;
345}
346
347static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
348 struct msghdr *m, size_t total_len)
349{
350 struct sock *sk = sock->sk;
351 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
352 struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
353 struct sk_buff_head *msg;
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530354 struct sk_buff *ipc_buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355 int ret;
356
357 if (!dest)
358 return -EDESTADDRREQ;
359
360 if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC)
361 return -EINVAL;
362
363 if (total_len > MAX_IPC_PKT_SIZE)
364 return -EINVAL;
365
366 lock_sock(sk);
367 msg = msm_ipc_router_build_msg(m->msg_iovlen, m->msg_iov, total_len);
368 if (!msg) {
369 pr_err("%s: Msg build failure\n", __func__);
370 ret = -ENOMEM;
371 goto out_sendmsg;
372 }
373
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -0700374 if (port_ptr->type == CLIENT_PORT)
375 wait_for_irsc_completion();
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530376 ipc_buf = skb_peek(msg);
Brent Hronik1c9a3d42013-04-17 15:10:40 -0600377 if (ipc_buf)
378 msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600380 if (ret != total_len) {
Zaheerulla Meer74425252013-08-20 12:02:31 +0530381 if (ret < 0) {
382 if (ret != -EAGAIN)
383 pr_err("%s: Send_to failure %d\n",
384 __func__, ret);
385 msm_ipc_router_free_skb(msg);
386 } else if (ret >= 0) {
Karthikeyan Ramasubramanian633fe382013-08-20 11:25:44 -0600387 ret = -EFAULT;
Zaheerulla Meer74425252013-08-20 12:02:31 +0530388 }
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600389 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700390
391out_sendmsg:
392 release_sock(sk);
393 return ret;
394}
395
396static int msm_ipc_router_recvmsg(struct kiocb *iocb, struct socket *sock,
397 struct msghdr *m, size_t buf_len, int flags)
398{
399 struct sock *sk = sock->sk;
400 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600401 struct rr_packet *pkt;
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530402 struct sk_buff *ipc_buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403 long timeout;
404 int ret;
405
406 if (m->msg_iovlen != 1)
407 return -EOPNOTSUPP;
408
409 if (!buf_len)
410 return -EINVAL;
411
412 lock_sock(sk);
413 timeout = sk->sk_rcvtimeo;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414
Arun Kumar Neelakantam0af96762013-06-21 17:57:18 +0530415 ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
416 if (ret) {
417 release_sock(sk);
418 if (ret == -ENOMSG)
Zaheerulla Meere3f6c3a2013-04-17 01:16:47 +0530419 m->msg_namelen = 0;
Arun Kumar Neelakantam0af96762013-06-21 17:57:18 +0530420 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600423 ret = msm_ipc_router_read(port_ptr, &pkt, buf_len);
424 if (ret <= 0 || !pkt) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425 release_sock(sk);
426 return ret;
427 }
428
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600429 ret = msm_ipc_router_extract_msg(m, pkt);
430 ipc_buf = skb_peek(pkt->pkt_fragment_q);
Brent Hronik1c9a3d42013-04-17 15:10:40 -0600431 if (ipc_buf)
432 msm_ipc_router_ipc_log(IPC_RECV, ipc_buf, port_ptr);
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600433 release_pkt(pkt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434 release_sock(sk);
435 return ret;
436}
437
438static int msm_ipc_router_ioctl(struct socket *sock,
439 unsigned int cmd, unsigned long arg)
440{
441 struct sock *sk = sock->sk;
442 struct msm_ipc_port *port_ptr;
443 struct server_lookup_args server_arg;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600444 struct msm_ipc_server_info *srv_info = NULL;
Karthikeyan Ramasubramanian29dfa702013-05-31 15:36:38 -0600445 unsigned int n;
446 size_t srv_info_sz = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447 int ret;
Karthikeyan Ramasubramanian88f02e52013-06-03 12:05:50 -0600448 void *pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449
450 if (!sk)
451 return -EINVAL;
452
453 lock_sock(sk);
454 port_ptr = msm_ipc_sk_port(sock->sk);
455 if (!port_ptr) {
456 release_sock(sk);
457 return -EINVAL;
458 }
459
460 switch (cmd) {
461 case IPC_ROUTER_IOCTL_GET_VERSION:
Karthikeyan Ramasubramanian7edb6802013-04-16 23:12:44 -0600462 n = IPC_ROUTER_V1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463 ret = put_user(n, (unsigned int *)arg);
464 break;
465
466 case IPC_ROUTER_IOCTL_GET_MTU:
467 n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
468 ret = put_user(n, (unsigned int *)arg);
469 break;
470
471 case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
472 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
473 break;
474
475 case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
Karthikeyan Ramasubramanian88f02e52013-06-03 12:05:50 -0600476 pil = msm_ipc_load_default_node();
477 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478 ret = copy_from_user(&server_arg, (void *)arg,
479 sizeof(server_arg));
480 if (ret) {
481 ret = -EFAULT;
482 break;
483 }
484
485 if (server_arg.num_entries_in_array < 0) {
486 ret = -EINVAL;
487 break;
488 }
489 if (server_arg.num_entries_in_array) {
Karthikeyan Ramasubramanian29dfa702013-05-31 15:36:38 -0600490 if (server_arg.num_entries_in_array >
491 (SIZE_MAX / sizeof(*srv_info))) {
Karthikeyan Ramasubramanianb34b6672013-02-26 16:37:50 -0700492 pr_err("%s: Integer Overflow %d * %d\n",
493 __func__, sizeof(*srv_info),
494 server_arg.num_entries_in_array);
495 ret = -EINVAL;
496 break;
497 }
Karthikeyan Ramasubramanian29dfa702013-05-31 15:36:38 -0600498 srv_info_sz = server_arg.num_entries_in_array *
499 sizeof(*srv_info);
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600500 srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
501 if (!srv_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502 ret = -ENOMEM;
503 break;
504 }
505 }
506 ret = msm_ipc_router_lookup_server_name(&server_arg.port_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600507 srv_info, server_arg.num_entries_in_array,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -0600508 server_arg.lookup_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509 if (ret < 0) {
510 pr_err("%s: Server not found\n", __func__);
511 ret = -ENODEV;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600512 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 break;
514 }
515 server_arg.num_entries_found = ret;
516
517 ret = copy_to_user((void *)arg, &server_arg,
518 sizeof(server_arg));
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600519 if (srv_info_sz) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700520 ret = copy_to_user((void *)(arg + sizeof(server_arg)),
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600521 srv_info, srv_info_sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522 if (ret)
523 ret = -EFAULT;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600524 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 }
526 break;
527
528 case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
529 ret = msm_ipc_router_bind_control_port(port_ptr);
530 break;
531
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600532 case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
533 ret = msm_ipc_config_sec_rules((void *)arg);
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -0700534 if (ret != -EPERM)
535 port_ptr->type = IRSC_PORT;
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600536 break;
537
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 default:
539 ret = -EINVAL;
540 }
541 release_sock(sk);
542 return ret;
543}
544
545static unsigned int msm_ipc_router_poll(struct file *file,
546 struct socket *sock, poll_table *wait)
547{
548 struct sock *sk = sock->sk;
549 struct msm_ipc_port *port_ptr;
550 uint32_t mask = 0;
551
552 if (!sk)
553 return -EINVAL;
554
555 port_ptr = msm_ipc_sk_port(sk);
556 if (!port_ptr)
557 return -EINVAL;
558
559 poll_wait(file, &port_ptr->port_rx_wait_q, wait);
560
561 if (!list_empty(&port_ptr->port_rx_q))
562 mask |= (POLLRDNORM | POLLIN);
563
564 return mask;
565}
566
567static int msm_ipc_router_close(struct socket *sock)
568{
569 struct sock *sk = sock->sk;
570 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
Karthikeyan Ramasubramanian88f02e52013-06-03 12:05:50 -0600571 void *pil = msm_ipc_sk(sk)->default_pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 int ret;
573
574 lock_sock(sk);
575 ret = msm_ipc_router_close_port(port_ptr);
Karthikeyan Ramasubramanian88f02e52013-06-03 12:05:50 -0600576 if (pil)
577 msm_ipc_unload_default_node(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578 release_sock(sk);
579 sock_put(sk);
580 sock->sk = NULL;
581
582 return ret;
583}
584
585static const struct net_proto_family msm_ipc_family_ops = {
586 .owner = THIS_MODULE,
587 .family = AF_MSM_IPC,
588 .create = msm_ipc_router_create
589};
590
591static const struct proto_ops msm_ipc_proto_ops = {
592 .owner = THIS_MODULE,
593 .family = AF_MSM_IPC,
594 .bind = msm_ipc_router_bind,
595 .connect = sock_no_connect,
596 .sendmsg = msm_ipc_router_sendmsg,
597 .recvmsg = msm_ipc_router_recvmsg,
598 .ioctl = msm_ipc_router_ioctl,
599 .poll = msm_ipc_router_poll,
600 .setsockopt = sock_no_setsockopt,
601 .getsockopt = sock_no_getsockopt,
602 .release = msm_ipc_router_close,
603};
604
605static struct proto msm_ipc_proto = {
606 .name = "MSM_IPC",
607 .owner = THIS_MODULE,
608 .obj_size = sizeof(struct msm_ipc_sock),
609};
610
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530611/**
612 * msm_ipc_router_ipc_log_init() - Init function for IPC Logging
613 *
614 * Initialize the buffers to be used to provide the log information
615 * pertaining to the request, response and indication data flow that
616 * happens between user and kernel spaces.
617 */
618void msm_ipc_router_ipc_log_init(void)
619{
620 ipc_req_resp_log_txt =
Zaheerulla Meera3cea662013-03-08 17:31:23 +0530621 ipc_log_context_create(REQ_RESP_IPC_LOG_PAGES,
622 "ipc_rtr_req_resp");
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530623 if (!ipc_req_resp_log_txt) {
624 pr_err("%s: Unable to create IPC logging for Req/Resp",
625 __func__);
626 }
627 ipc_ind_log_txt =
Zaheerulla Meera3cea662013-03-08 17:31:23 +0530628 ipc_log_context_create(IND_IPC_LOG_PAGES, "ipc_rtr_ind");
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530629 if (!ipc_ind_log_txt) {
630 pr_err("%s: Unable to create IPC logging for Indications",
631 __func__);
632 }
633}
634
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700635int msm_ipc_router_init_sockets(void)
636{
637 int ret;
638
639 ret = proto_register(&msm_ipc_proto, 1);
640 if (ret) {
641 pr_err("Failed to register MSM_IPC protocol type\n");
642 goto out_init_sockets;
643 }
644
645 ret = sock_register(&msm_ipc_family_ops);
646 if (ret) {
647 pr_err("Failed to register MSM_IPC socket type\n");
648 proto_unregister(&msm_ipc_proto);
649 goto out_init_sockets;
650 }
651
652 sockets_enabled = 1;
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530653 msm_ipc_router_ipc_log_init();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700654out_init_sockets:
655 return ret;
656}
657
658void msm_ipc_router_exit_sockets(void)
659{
660 if (!sockets_enabled)
661 return;
662
663 sockets_enabled = 0;
664 sock_unregister(msm_ipc_family_ops.family);
665 proto_unregister(&msm_ipc_proto);
666}