blob: 342663e18598c1670d2f0b09461699e323239bf3 [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
58static int sockets_enabled;
59static struct proto msm_ipc_proto;
60static const struct proto_ops msm_ipc_proto_ops;
Zaheerulla Meerba10d372013-02-21 17:56:18 +053061static void *ipc_req_resp_log_txt;
62static void *ipc_ind_log_txt;
63
64/**
65 * msm_ipc_router_ipc_log() - Pass log data to IPC logging framework
66 * @tran: Identifies the data to be a receive or send.
67 * @ipc_buf: Buffer to extract the log data.
68 * @port_ptr: IPC Router port corresponding to the current log data.
69 *
70 * This function builds the data the would be passed on to the IPC logging
71 * framework. The data that would be passed corresponds to the information
72 * that is exchanged between the IPC Router and user space modules during
73 * request/response/indication transactions.
74 */
75
76static void msm_ipc_router_ipc_log(uint8_t tran,
77 struct sk_buff *ipc_buf, struct msm_ipc_port *port_ptr)
78{
79 struct qmi_header *hdr = (struct qmi_header *)ipc_buf->data;
80
81 /*
82 * IPC Logging format is as below:-
83 * <Name>(Name of the User Space Process):
84 * <PID> (PID of the user space process) :
85 * <TID> (TID of the user space thread) :
86 * <User Space Module>(CLNT or SERV) :
87 * <Opertaion Type> (Transmit) :
88 * <Control Flag> (Req/Resp/Ind) :
89 * <Transaction ID> :
90 * <Message ID> :
91 * <Message Length> :
92 */
93 if (ipc_req_resp_log_txt &&
94 (((uint8_t) hdr->cntl_flag == QMI_REQUEST_CONTROL_FLAG) ||
95 ((uint8_t) hdr->cntl_flag == QMI_RESPONSE_CONTROL_FLAG)) &&
96 (port_ptr->type == CLIENT_PORT ||
97 port_ptr->type == SERVER_PORT)) {
98 IPC_REQ_RESP_LOG(KERN_DEBUG,
99 "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
100 current->comm, current->tgid, current->pid,
101 (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
102 (tran == IPC_RECV ? "RX" :
103 (tran == IPC_SEND ? "TX" : "ERR")),
104 (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
105 hdr->msg_len);
106 } else if (ipc_ind_log_txt &&
107 ((uint8_t)hdr->cntl_flag == QMI_INDICATION_CONTROL_FLAG) &&
108 (port_ptr->type == CLIENT_PORT ||
109 port_ptr->type == SERVER_PORT)) {
110 IPC_IND_LOG(KERN_DEBUG,
111 "%s %d %d %s %s CF:%x TI:%x MI:%x ML:%x",
112 current->comm, current->tgid, current->pid,
113 (port_ptr->type == CLIENT_PORT ? "QCCI" : "QCSI"),
114 (tran == IPC_RECV ? "RX" :
115 (tran == IPC_SEND ? "TX" : "ERR")),
116 (uint8_t)hdr->cntl_flag, hdr->txn_id, hdr->msg_id,
117 hdr->msg_len);
118 }
119}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700120
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700121static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
122 struct iovec const *msg_sect,
123 size_t total_len)
124{
125 struct sk_buff_head *msg_head;
126 struct sk_buff *msg;
127 int i, copied, first = 1;
128 int data_size = 0, request_size, offset;
129 void *data;
130
131 for (i = 0; i < num_sect; i++)
132 data_size += msg_sect[i].iov_len;
133
134 if (!data_size)
135 return NULL;
136
137 msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
138 if (!msg_head) {
139 pr_err("%s: cannot allocate skb_head\n", __func__);
140 return NULL;
141 }
142 skb_queue_head_init(msg_head);
143
144 for (copied = 1, i = 0; copied && (i < num_sect); i++) {
145 data_size = msg_sect[i].iov_len;
146 offset = 0;
147 while (offset != msg_sect[i].iov_len) {
148 request_size = data_size;
149 if (first)
150 request_size += IPC_ROUTER_HDR_SIZE;
151
152 msg = alloc_skb(request_size, GFP_KERNEL);
153 if (!msg) {
154 if (request_size <= (PAGE_SIZE/2)) {
155 pr_err("%s: cannot allocated skb\n",
156 __func__);
157 goto msg_build_failure;
158 }
159 data_size = data_size / 2;
160 continue;
161 }
162
163 if (first) {
164 skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
165 first = 0;
166 }
167
168 data = skb_put(msg, data_size);
169 copied = !copy_from_user(msg->data,
170 msg_sect[i].iov_base + offset,
171 data_size);
172 if (!copied) {
173 pr_err("%s: copy_from_user failed\n",
174 __func__);
175 kfree_skb(msg);
176 goto msg_build_failure;
177 }
178 skb_queue_tail(msg_head, msg);
179 offset += data_size;
180 data_size = msg_sect[i].iov_len - offset;
181 }
182 }
183 return msg_head;
184
185msg_build_failure:
186 while (!skb_queue_empty(msg_head)) {
187 msg = skb_dequeue(msg_head);
188 kfree_skb(msg);
189 }
190 kfree(msg_head);
191 return NULL;
192}
193
194static int msm_ipc_router_extract_msg(struct msghdr *m,
195 struct sk_buff_head *msg_head)
196{
Karthikeyan Ramasubramanian4d1695b2013-04-05 11:30:53 -0600197 struct sockaddr_msm_ipc *addr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 struct rr_header *hdr;
199 struct sk_buff *temp;
200 int offset = 0, data_len = 0, copy_len;
201
202 if (!m || !msg_head) {
203 pr_err("%s: Invalid pointers passed\n", __func__);
204 return -EINVAL;
205 }
Karthikeyan Ramasubramanian4d1695b2013-04-05 11:30:53 -0600206 addr = (struct sockaddr_msm_ipc *)m->msg_name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700207
208 temp = skb_peek(msg_head);
209 hdr = (struct rr_header *)(temp->data);
Karthikeyan Ramasubramanian4d1695b2013-04-05 11:30:53 -0600210 if (addr && (hdr->src_port_id != IPC_ROUTER_ADDRESS)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700211 addr->family = AF_MSM_IPC;
212 addr->address.addrtype = MSM_IPC_ADDR_ID;
213 addr->address.addr.port_addr.node_id = hdr->src_node_id;
214 addr->address.addr.port_addr.port_id = hdr->src_port_id;
215 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
216 }
217
218 data_len = hdr->size;
219 skb_pull(temp, IPC_ROUTER_HDR_SIZE);
220 skb_queue_walk(msg_head, temp) {
221 copy_len = data_len < temp->len ? data_len : temp->len;
222 if (copy_to_user(m->msg_iov->iov_base + offset, temp->data,
223 copy_len)) {
224 pr_err("%s: Copy to user failed\n", __func__);
225 return -EFAULT;
226 }
227 offset += copy_len;
228 data_len -= copy_len;
229 }
230 return offset;
231}
232
233static void msm_ipc_router_release_msg(struct sk_buff_head *msg_head)
234{
235 struct sk_buff *temp;
236
237 if (!msg_head) {
238 pr_err("%s: Invalid msg pointer\n", __func__);
239 return;
240 }
241
242 while (!skb_queue_empty(msg_head)) {
243 temp = skb_dequeue(msg_head);
244 kfree_skb(temp);
245 }
246 kfree(msg_head);
247}
248
249static int msm_ipc_router_create(struct net *net,
250 struct socket *sock,
251 int protocol,
252 int kern)
253{
254 struct sock *sk;
255 struct msm_ipc_port *port_ptr;
256 void *pil;
257
258 if (unlikely(protocol != 0)) {
259 pr_err("%s: Protocol not supported\n", __func__);
260 return -EPROTONOSUPPORT;
261 }
262
263 switch (sock->type) {
264 case SOCK_DGRAM:
265 break;
266 default:
267 pr_err("%s: Protocol type not supported\n", __func__);
268 return -EPROTOTYPE;
269 }
270
271 sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto);
272 if (!sk) {
273 pr_err("%s: sk_alloc failed\n", __func__);
274 return -ENOMEM;
275 }
276
277 port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
278 if (!port_ptr) {
279 pr_err("%s: port_ptr alloc failed\n", __func__);
280 sk_free(sk);
281 return -ENOMEM;
282 }
283
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600284 port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285 sock->ops = &msm_ipc_proto_ops;
286 sock_init_data(sock, sk);
287 sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
288
Karthikeyan Ramasubramanian6b963bd2012-05-01 11:27:54 -0600289 pil = msm_ipc_load_default_node();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700290 msm_ipc_sk(sk)->port = port_ptr;
Karthikeyan Ramasubramanian6b963bd2012-05-01 11:27:54 -0600291 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700292
293 return 0;
294}
295
296int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
297 int uaddr_len)
298{
299 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
300 struct sock *sk = sock->sk;
301 struct msm_ipc_port *port_ptr;
302 int ret;
303
304 if (!sk)
305 return -EINVAL;
306
Karthikeyan Ramasubramanian13ef0552013-01-24 11:45:41 -0700307 if (!check_permissions()) {
308 pr_err("%s: %s Do not have permissions\n",
309 __func__, current->comm);
310 return -EPERM;
311 }
312
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313 if (!uaddr_len) {
314 pr_err("%s: Invalid address length\n", __func__);
315 return -EINVAL;
316 }
317
318 if (addr->family != AF_MSM_IPC) {
319 pr_err("%s: Address family is incorrect\n", __func__);
320 return -EAFNOSUPPORT;
321 }
322
323 if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
324 pr_err("%s: Address type is incorrect\n", __func__);
325 return -EINVAL;
326 }
327
328 port_ptr = msm_ipc_sk_port(sk);
329 if (!port_ptr)
330 return -ENODEV;
331
332 lock_sock(sk);
333
334 ret = msm_ipc_router_register_server(port_ptr, &addr->address);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335
336 release_sock(sk);
337 return ret;
338}
339
340static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
341 struct msghdr *m, size_t total_len)
342{
343 struct sock *sk = sock->sk;
344 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
345 struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
346 struct sk_buff_head *msg;
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530347 struct sk_buff *ipc_buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 int ret;
349
350 if (!dest)
351 return -EDESTADDRREQ;
352
353 if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC)
354 return -EINVAL;
355
356 if (total_len > MAX_IPC_PKT_SIZE)
357 return -EINVAL;
358
359 lock_sock(sk);
360 msg = msm_ipc_router_build_msg(m->msg_iovlen, m->msg_iov, total_len);
361 if (!msg) {
362 pr_err("%s: Msg build failure\n", __func__);
363 ret = -ENOMEM;
364 goto out_sendmsg;
365 }
366
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -0700367 if (port_ptr->type == CLIENT_PORT)
368 wait_for_irsc_completion();
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530369 ipc_buf = skb_peek(msg);
Brent Hronik1c9a3d42013-04-17 15:10:40 -0600370 if (ipc_buf)
371 msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
373 if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
374 ret = total_len;
375
376out_sendmsg:
377 release_sock(sk);
378 return ret;
379}
380
381static int msm_ipc_router_recvmsg(struct kiocb *iocb, struct socket *sock,
382 struct msghdr *m, size_t buf_len, int flags)
383{
384 struct sock *sk = sock->sk;
385 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
386 struct sk_buff_head *msg;
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530387 struct sk_buff *ipc_buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 long timeout;
389 int ret;
390
391 if (m->msg_iovlen != 1)
392 return -EOPNOTSUPP;
393
394 if (!buf_len)
395 return -EINVAL;
396
397 lock_sock(sk);
398 timeout = sk->sk_rcvtimeo;
399 mutex_lock(&port_ptr->port_rx_q_lock);
400 while (list_empty(&port_ptr->port_rx_q)) {
401 mutex_unlock(&port_ptr->port_rx_q_lock);
402 release_sock(sk);
403 if (timeout < 0) {
404 ret = wait_event_interruptible(
405 port_ptr->port_rx_wait_q,
406 !list_empty(&port_ptr->port_rx_q));
407 if (ret)
408 return ret;
409 } else if (timeout > 0) {
410 timeout = wait_event_interruptible_timeout(
411 port_ptr->port_rx_wait_q,
412 !list_empty(&port_ptr->port_rx_q),
413 timeout);
414 if (timeout < 0)
415 return -EFAULT;
416 }
417
418 if (timeout == 0)
Karthikeyan Ramasubramanianb7029232013-03-18 11:52:46 -0600419 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420 lock_sock(sk);
421 mutex_lock(&port_ptr->port_rx_q_lock);
422 }
423 mutex_unlock(&port_ptr->port_rx_q_lock);
424
425 ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
426 if (ret <= 0 || !msg) {
427 release_sock(sk);
428 return ret;
429 }
430
431 ret = msm_ipc_router_extract_msg(m, msg);
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530432 ipc_buf = skb_peek(msg);
Brent Hronik1c9a3d42013-04-17 15:10:40 -0600433 if (ipc_buf)
434 msm_ipc_router_ipc_log(IPC_RECV, ipc_buf, port_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 msm_ipc_router_release_msg(msg);
436 msg = NULL;
437 release_sock(sk);
438 return ret;
439}
440
441static int msm_ipc_router_ioctl(struct socket *sock,
442 unsigned int cmd, unsigned long arg)
443{
444 struct sock *sk = sock->sk;
445 struct msm_ipc_port *port_ptr;
446 struct server_lookup_args server_arg;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600447 struct msm_ipc_server_info *srv_info = NULL;
448 unsigned int n, srv_info_sz = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 int ret;
450
451 if (!sk)
452 return -EINVAL;
453
454 lock_sock(sk);
455 port_ptr = msm_ipc_sk_port(sock->sk);
456 if (!port_ptr) {
457 release_sock(sk);
458 return -EINVAL;
459 }
460
461 switch (cmd) {
462 case IPC_ROUTER_IOCTL_GET_VERSION:
463 n = IPC_ROUTER_VERSION;
464 ret = put_user(n, (unsigned int *)arg);
465 break;
466
467 case IPC_ROUTER_IOCTL_GET_MTU:
468 n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
469 ret = put_user(n, (unsigned int *)arg);
470 break;
471
472 case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
473 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
474 break;
475
476 case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
477 ret = copy_from_user(&server_arg, (void *)arg,
478 sizeof(server_arg));
479 if (ret) {
480 ret = -EFAULT;
481 break;
482 }
483
484 if (server_arg.num_entries_in_array < 0) {
485 ret = -EINVAL;
486 break;
487 }
488 if (server_arg.num_entries_in_array) {
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600489 srv_info_sz = server_arg.num_entries_in_array *
490 sizeof(*srv_info);
Karthikeyan Ramasubramanianb34b6672013-02-26 16:37:50 -0700491 if ((srv_info_sz / sizeof(*srv_info)) !=
492 server_arg.num_entries_in_array) {
493 pr_err("%s: Integer Overflow %d * %d\n",
494 __func__, sizeof(*srv_info),
495 server_arg.num_entries_in_array);
496 ret = -EINVAL;
497 break;
498 }
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600499 srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
500 if (!srv_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700501 ret = -ENOMEM;
502 break;
503 }
504 }
505 ret = msm_ipc_router_lookup_server_name(&server_arg.port_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600506 srv_info, server_arg.num_entries_in_array,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -0600507 server_arg.lookup_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508 if (ret < 0) {
509 pr_err("%s: Server not found\n", __func__);
510 ret = -ENODEV;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600511 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700512 break;
513 }
514 server_arg.num_entries_found = ret;
515
516 ret = copy_to_user((void *)arg, &server_arg,
517 sizeof(server_arg));
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600518 if (srv_info_sz) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 ret = copy_to_user((void *)(arg + sizeof(server_arg)),
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600520 srv_info, srv_info_sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 if (ret)
522 ret = -EFAULT;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600523 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524 }
525 break;
526
527 case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
528 ret = msm_ipc_router_bind_control_port(port_ptr);
529 break;
530
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600531 case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
532 ret = msm_ipc_config_sec_rules((void *)arg);
Karthikeyan Ramasubramanianf7a4b6e2013-01-16 09:00:28 -0700533 if (ret != -EPERM)
534 port_ptr->type = IRSC_PORT;
Karthikeyan Ramasubramanian5b502d3642012-09-23 22:23:36 -0600535 break;
536
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537 default:
538 ret = -EINVAL;
539 }
540 release_sock(sk);
541 return ret;
542}
543
544static unsigned int msm_ipc_router_poll(struct file *file,
545 struct socket *sock, poll_table *wait)
546{
547 struct sock *sk = sock->sk;
548 struct msm_ipc_port *port_ptr;
549 uint32_t mask = 0;
550
551 if (!sk)
552 return -EINVAL;
553
554 port_ptr = msm_ipc_sk_port(sk);
555 if (!port_ptr)
556 return -EINVAL;
557
558 poll_wait(file, &port_ptr->port_rx_wait_q, wait);
559
560 if (!list_empty(&port_ptr->port_rx_q))
561 mask |= (POLLRDNORM | POLLIN);
562
563 return mask;
564}
565
566static int msm_ipc_router_close(struct socket *sock)
567{
568 struct sock *sk = sock->sk;
569 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
Karthikeyan Ramasubramanian6b963bd2012-05-01 11:27:54 -0600570 void *pil = msm_ipc_sk(sk)->default_pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 int ret;
572
573 lock_sock(sk);
574 ret = msm_ipc_router_close_port(port_ptr);
Karthikeyan Ramasubramanian6b963bd2012-05-01 11:27:54 -0600575 msm_ipc_unload_default_node(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576 release_sock(sk);
577 sock_put(sk);
578 sock->sk = NULL;
579
580 return ret;
581}
582
583static const struct net_proto_family msm_ipc_family_ops = {
584 .owner = THIS_MODULE,
585 .family = AF_MSM_IPC,
586 .create = msm_ipc_router_create
587};
588
589static const struct proto_ops msm_ipc_proto_ops = {
590 .owner = THIS_MODULE,
591 .family = AF_MSM_IPC,
592 .bind = msm_ipc_router_bind,
593 .connect = sock_no_connect,
594 .sendmsg = msm_ipc_router_sendmsg,
595 .recvmsg = msm_ipc_router_recvmsg,
596 .ioctl = msm_ipc_router_ioctl,
597 .poll = msm_ipc_router_poll,
598 .setsockopt = sock_no_setsockopt,
599 .getsockopt = sock_no_getsockopt,
600 .release = msm_ipc_router_close,
601};
602
603static struct proto msm_ipc_proto = {
604 .name = "MSM_IPC",
605 .owner = THIS_MODULE,
606 .obj_size = sizeof(struct msm_ipc_sock),
607};
608
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530609/**
610 * msm_ipc_router_ipc_log_init() - Init function for IPC Logging
611 *
612 * Initialize the buffers to be used to provide the log information
613 * pertaining to the request, response and indication data flow that
614 * happens between user and kernel spaces.
615 */
616void msm_ipc_router_ipc_log_init(void)
617{
618 ipc_req_resp_log_txt =
Zaheerulla Meera3cea662013-03-08 17:31:23 +0530619 ipc_log_context_create(REQ_RESP_IPC_LOG_PAGES,
620 "ipc_rtr_req_resp");
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530621 if (!ipc_req_resp_log_txt) {
622 pr_err("%s: Unable to create IPC logging for Req/Resp",
623 __func__);
624 }
625 ipc_ind_log_txt =
Zaheerulla Meera3cea662013-03-08 17:31:23 +0530626 ipc_log_context_create(IND_IPC_LOG_PAGES, "ipc_rtr_ind");
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530627 if (!ipc_ind_log_txt) {
628 pr_err("%s: Unable to create IPC logging for Indications",
629 __func__);
630 }
631}
632
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633int msm_ipc_router_init_sockets(void)
634{
635 int ret;
636
637 ret = proto_register(&msm_ipc_proto, 1);
638 if (ret) {
639 pr_err("Failed to register MSM_IPC protocol type\n");
640 goto out_init_sockets;
641 }
642
643 ret = sock_register(&msm_ipc_family_ops);
644 if (ret) {
645 pr_err("Failed to register MSM_IPC socket type\n");
646 proto_unregister(&msm_ipc_proto);
647 goto out_init_sockets;
648 }
649
650 sockets_enabled = 1;
Zaheerulla Meerba10d372013-02-21 17:56:18 +0530651 msm_ipc_router_ipc_log_init();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700652out_init_sockets:
653 return ret;
654}
655
656void msm_ipc_router_exit_sockets(void)
657{
658 if (!sockets_enabled)
659 return;
660
661 sockets_enabled = 0;
662 sock_unregister(msm_ipc_family_ops.family);
663 proto_unregister(&msm_ipc_proto);
664}