blob: 515dc92bb9b64098f611663c71316b972945a630 [file] [log] [blame]
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -06001/* 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 Meerf0707d52013-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 Ramasubramanian70444192012-07-12 10:25:49 -060032#include <mach/msm_ipc_router.h>
Zaheerulla Meerf0707d52013-02-21 17:56:18 +053033#include <mach/msm_ipc_logging.h>
Karthikeyan Ramasubramanian70444192012-07-12 10:25:49 -060034
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#include "ipc_router.h"
Karthikeyan Ramasubramanian1668cc62012-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 Meerf0707d52013-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 Ramasubramaniand28b3402013-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 Meerf0707d52013-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;
134
135 for (i = 0; i < num_sect; i++)
136 data_size += msg_sect[i].iov_len;
137
138 if (!data_size)
139 return NULL;
140
141 msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
142 if (!msg_head) {
143 pr_err("%s: cannot allocate skb_head\n", __func__);
144 return NULL;
145 }
146 skb_queue_head_init(msg_head);
147
148 for (copied = 1, i = 0; copied && (i < num_sect); i++) {
149 data_size = msg_sect[i].iov_len;
150 offset = 0;
151 while (offset != msg_sect[i].iov_len) {
152 request_size = data_size;
153 if (first)
154 request_size += IPC_ROUTER_HDR_SIZE;
155
156 msg = alloc_skb(request_size, GFP_KERNEL);
157 if (!msg) {
158 if (request_size <= (PAGE_SIZE/2)) {
159 pr_err("%s: cannot allocated skb\n",
160 __func__);
161 goto msg_build_failure;
162 }
163 data_size = data_size / 2;
164 continue;
165 }
166
167 if (first) {
168 skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
169 first = 0;
170 }
171
172 data = skb_put(msg, data_size);
173 copied = !copy_from_user(msg->data,
174 msg_sect[i].iov_base + offset,
175 data_size);
176 if (!copied) {
177 pr_err("%s: copy_from_user failed\n",
178 __func__);
179 kfree_skb(msg);
180 goto msg_build_failure;
181 }
182 skb_queue_tail(msg_head, msg);
183 offset += data_size;
184 data_size = msg_sect[i].iov_len - offset;
185 }
186 }
187 return msg_head;
188
189msg_build_failure:
190 while (!skb_queue_empty(msg_head)) {
191 msg = skb_dequeue(msg_head);
192 kfree_skb(msg);
193 }
194 kfree(msg_head);
195 return NULL;
196}
197
198static int msm_ipc_router_extract_msg(struct msghdr *m,
199 struct sk_buff_head *msg_head)
200{
Karthikeyan Ramasubramanian2272b882013-04-05 11:30:53 -0600201 struct sockaddr_msm_ipc *addr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 struct rr_header *hdr;
203 struct sk_buff *temp;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530204 union rr_control_msg *ctl_msg;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 int offset = 0, data_len = 0, copy_len;
206
207 if (!m || !msg_head) {
208 pr_err("%s: Invalid pointers passed\n", __func__);
209 return -EINVAL;
210 }
Karthikeyan Ramasubramanian2272b882013-04-05 11:30:53 -0600211 addr = (struct sockaddr_msm_ipc *)m->msg_name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212
213 temp = skb_peek(msg_head);
214 hdr = (struct rr_header *)(temp->data);
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530215 if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
216 skb_pull(temp, IPC_ROUTER_HDR_SIZE);
217 ctl_msg = (union rr_control_msg *)(temp->data);
218 addr->family = AF_MSM_IPC;
219 addr->address.addrtype = MSM_IPC_ADDR_ID;
220 addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id;
221 addr->address.addr.port_addr.port_id = ctl_msg->cli.port_id;
222 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
223 return offset;
224 }
Karthikeyan Ramasubramanian2272b882013-04-05 11:30:53 -0600225 if (addr && (hdr->src_port_id != IPC_ROUTER_ADDRESS)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 addr->family = AF_MSM_IPC;
227 addr->address.addrtype = MSM_IPC_ADDR_ID;
228 addr->address.addr.port_addr.node_id = hdr->src_node_id;
229 addr->address.addr.port_addr.port_id = hdr->src_port_id;
230 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
231 }
232
233 data_len = hdr->size;
234 skb_pull(temp, IPC_ROUTER_HDR_SIZE);
235 skb_queue_walk(msg_head, temp) {
236 copy_len = data_len < temp->len ? data_len : temp->len;
237 if (copy_to_user(m->msg_iov->iov_base + offset, temp->data,
238 copy_len)) {
239 pr_err("%s: Copy to user failed\n", __func__);
240 return -EFAULT;
241 }
242 offset += copy_len;
243 data_len -= copy_len;
244 }
245 return offset;
246}
247
248static void msm_ipc_router_release_msg(struct sk_buff_head *msg_head)
249{
250 struct sk_buff *temp;
251
252 if (!msg_head) {
253 pr_err("%s: Invalid msg pointer\n", __func__);
254 return;
255 }
256
257 while (!skb_queue_empty(msg_head)) {
258 temp = skb_dequeue(msg_head);
259 kfree_skb(temp);
260 }
261 kfree(msg_head);
262}
263
264static int msm_ipc_router_create(struct net *net,
265 struct socket *sock,
266 int protocol,
267 int kern)
268{
269 struct sock *sk;
270 struct msm_ipc_port *port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271
272 if (unlikely(protocol != 0)) {
273 pr_err("%s: Protocol not supported\n", __func__);
274 return -EPROTONOSUPPORT;
275 }
276
277 switch (sock->type) {
278 case SOCK_DGRAM:
279 break;
280 default:
281 pr_err("%s: Protocol type not supported\n", __func__);
282 return -EPROTOTYPE;
283 }
284
285 sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto);
286 if (!sk) {
287 pr_err("%s: sk_alloc failed\n", __func__);
288 return -ENOMEM;
289 }
290
291 port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
292 if (!port_ptr) {
293 pr_err("%s: port_ptr alloc failed\n", __func__);
294 sk_free(sk);
295 return -ENOMEM;
296 }
297
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600298 port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700299 sock->ops = &msm_ipc_proto_ops;
300 sock_init_data(sock, sk);
301 sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
302
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700303 msm_ipc_sk(sk)->port = port_ptr;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
305 return 0;
306}
307
308int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
309 int uaddr_len)
310{
311 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
312 struct sock *sk = sock->sk;
313 struct msm_ipc_port *port_ptr;
314 int ret;
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600315 void *pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316
317 if (!sk)
318 return -EINVAL;
319
Karthikeyan Ramasubramanian54426332013-01-24 11:45:41 -0700320 if (!check_permissions()) {
321 pr_err("%s: %s Do not have permissions\n",
322 __func__, current->comm);
323 return -EPERM;
324 }
325
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 if (!uaddr_len) {
327 pr_err("%s: Invalid address length\n", __func__);
328 return -EINVAL;
329 }
330
331 if (addr->family != AF_MSM_IPC) {
332 pr_err("%s: Address family is incorrect\n", __func__);
333 return -EAFNOSUPPORT;
334 }
335
336 if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
337 pr_err("%s: Address type is incorrect\n", __func__);
338 return -EINVAL;
339 }
340
341 port_ptr = msm_ipc_sk_port(sk);
342 if (!port_ptr)
343 return -ENODEV;
344
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600345 pil = msm_ipc_load_default_node();
346 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700347 lock_sock(sk);
348
349 ret = msm_ipc_router_register_server(port_ptr, &addr->address);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700350
351 release_sock(sk);
352 return ret;
353}
354
355static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
356 struct msghdr *m, size_t total_len)
357{
358 struct sock *sk = sock->sk;
359 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
360 struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
361 struct sk_buff_head *msg;
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530362 struct sk_buff *ipc_buf;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700363 int ret;
364
365 if (!dest)
366 return -EDESTADDRREQ;
367
368 if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC)
369 return -EINVAL;
370
371 if (total_len > MAX_IPC_PKT_SIZE)
372 return -EINVAL;
373
374 lock_sock(sk);
375 msg = msm_ipc_router_build_msg(m->msg_iovlen, m->msg_iov, total_len);
376 if (!msg) {
377 pr_err("%s: Msg build failure\n", __func__);
378 ret = -ENOMEM;
379 goto out_sendmsg;
380 }
381
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -0700382 if (port_ptr->type == CLIENT_PORT)
383 wait_for_irsc_completion();
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530384 ipc_buf = skb_peek(msg);
Brent Hronik3d6d977d2013-04-17 15:10:40 -0600385 if (ipc_buf)
386 msm_ipc_router_ipc_log(IPC_SEND, ipc_buf, port_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
388 if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
389 ret = total_len;
390
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);
401 struct sk_buff_head *msg;
Zaheerulla Meerf0707d52013-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;
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600414 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700415 while (list_empty(&port_ptr->port_rx_q)) {
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600416 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417 release_sock(sk);
418 if (timeout < 0) {
419 ret = wait_event_interruptible(
420 port_ptr->port_rx_wait_q,
421 !list_empty(&port_ptr->port_rx_q));
422 if (ret)
423 return ret;
424 } else if (timeout > 0) {
425 timeout = wait_event_interruptible_timeout(
426 port_ptr->port_rx_wait_q,
427 !list_empty(&port_ptr->port_rx_q),
428 timeout);
429 if (timeout < 0)
430 return -EFAULT;
431 }
432
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530433 if (timeout == 0) {
434 m->msg_namelen = 0;
Karthikeyan Ramasubramanian29f023b2013-03-18 11:52:46 -0600435 return 0;
Zaheerulla Meera34fc662013-04-17 01:16:47 +0530436 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 lock_sock(sk);
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600438 mutex_lock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700439 }
Karthikeyan Ramasubramaniand06fe072013-05-21 11:49:21 -0600440 mutex_unlock(&port_ptr->port_rx_q_lock_lhb3);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441
442 ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
443 if (ret <= 0 || !msg) {
444 release_sock(sk);
445 return ret;
446 }
447
448 ret = msm_ipc_router_extract_msg(m, msg);
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530449 ipc_buf = skb_peek(msg);
Brent Hronik3d6d977d2013-04-17 15:10:40 -0600450 if (ipc_buf)
451 msm_ipc_router_ipc_log(IPC_RECV, ipc_buf, port_ptr);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 msm_ipc_router_release_msg(msg);
453 msg = NULL;
454 release_sock(sk);
455 return ret;
456}
457
458static int msm_ipc_router_ioctl(struct socket *sock,
459 unsigned int cmd, unsigned long arg)
460{
461 struct sock *sk = sock->sk;
462 struct msm_ipc_port *port_ptr;
463 struct server_lookup_args server_arg;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600464 struct msm_ipc_server_info *srv_info = NULL;
Karthikeyan Ramasubramaniand28b3402013-05-31 15:36:38 -0600465 unsigned int n;
466 size_t srv_info_sz = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 int ret;
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600468 void *pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469
470 if (!sk)
471 return -EINVAL;
472
473 lock_sock(sk);
474 port_ptr = msm_ipc_sk_port(sock->sk);
475 if (!port_ptr) {
476 release_sock(sk);
477 return -EINVAL;
478 }
479
480 switch (cmd) {
481 case IPC_ROUTER_IOCTL_GET_VERSION:
482 n = IPC_ROUTER_VERSION;
483 ret = put_user(n, (unsigned int *)arg);
484 break;
485
486 case IPC_ROUTER_IOCTL_GET_MTU:
487 n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
488 ret = put_user(n, (unsigned int *)arg);
489 break;
490
491 case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
492 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
493 break;
494
495 case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600496 pil = msm_ipc_load_default_node();
497 msm_ipc_sk(sk)->default_pil = pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700498 ret = copy_from_user(&server_arg, (void *)arg,
499 sizeof(server_arg));
500 if (ret) {
501 ret = -EFAULT;
502 break;
503 }
504
505 if (server_arg.num_entries_in_array < 0) {
506 ret = -EINVAL;
507 break;
508 }
509 if (server_arg.num_entries_in_array) {
Karthikeyan Ramasubramaniand28b3402013-05-31 15:36:38 -0600510 if (server_arg.num_entries_in_array >
511 (SIZE_MAX / sizeof(*srv_info))) {
Karthikeyan Ramasubramanianabe0b062013-02-26 16:37:50 -0700512 pr_err("%s: Integer Overflow %d * %d\n",
513 __func__, sizeof(*srv_info),
514 server_arg.num_entries_in_array);
515 ret = -EINVAL;
516 break;
517 }
Karthikeyan Ramasubramaniand28b3402013-05-31 15:36:38 -0600518 srv_info_sz = server_arg.num_entries_in_array *
519 sizeof(*srv_info);
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600520 srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
521 if (!srv_info) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522 ret = -ENOMEM;
523 break;
524 }
525 }
526 ret = msm_ipc_router_lookup_server_name(&server_arg.port_name,
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600527 srv_info, server_arg.num_entries_in_array,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -0600528 server_arg.lookup_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700529 if (ret < 0) {
530 pr_err("%s: Server not found\n", __func__);
531 ret = -ENODEV;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600532 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700533 break;
534 }
535 server_arg.num_entries_found = ret;
536
537 ret = copy_to_user((void *)arg, &server_arg,
538 sizeof(server_arg));
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600539 if (srv_info_sz) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 ret = copy_to_user((void *)(arg + sizeof(server_arg)),
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600541 srv_info, srv_info_sz);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700542 if (ret)
543 ret = -EFAULT;
Karthikeyan Ramasubramaniandfde01b2012-06-12 14:25:13 -0600544 kfree(srv_info);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545 }
546 break;
547
548 case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
549 ret = msm_ipc_router_bind_control_port(port_ptr);
550 break;
551
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600552 case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
553 ret = msm_ipc_config_sec_rules((void *)arg);
Karthikeyan Ramasubramaniand6dbfae2013-01-16 09:00:28 -0700554 if (ret != -EPERM)
555 port_ptr->type = IRSC_PORT;
Karthikeyan Ramasubramanian1668cc62012-09-23 22:23:36 -0600556 break;
557
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700558 default:
559 ret = -EINVAL;
560 }
561 release_sock(sk);
562 return ret;
563}
564
565static unsigned int msm_ipc_router_poll(struct file *file,
566 struct socket *sock, poll_table *wait)
567{
568 struct sock *sk = sock->sk;
569 struct msm_ipc_port *port_ptr;
570 uint32_t mask = 0;
571
572 if (!sk)
573 return -EINVAL;
574
575 port_ptr = msm_ipc_sk_port(sk);
576 if (!port_ptr)
577 return -EINVAL;
578
579 poll_wait(file, &port_ptr->port_rx_wait_q, wait);
580
581 if (!list_empty(&port_ptr->port_rx_q))
582 mask |= (POLLRDNORM | POLLIN);
583
584 return mask;
585}
586
587static int msm_ipc_router_close(struct socket *sock)
588{
589 struct sock *sk = sock->sk;
590 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600591 void *pil = msm_ipc_sk(sk)->default_pil;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700592 int ret;
593
594 lock_sock(sk);
595 ret = msm_ipc_router_close_port(port_ptr);
Karthikeyan Ramasubramanianaf90ba82013-06-03 12:05:50 -0600596 if (pil)
597 msm_ipc_unload_default_node(pil);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598 release_sock(sk);
599 sock_put(sk);
600 sock->sk = NULL;
601
602 return ret;
603}
604
605static const struct net_proto_family msm_ipc_family_ops = {
606 .owner = THIS_MODULE,
607 .family = AF_MSM_IPC,
608 .create = msm_ipc_router_create
609};
610
611static const struct proto_ops msm_ipc_proto_ops = {
612 .owner = THIS_MODULE,
613 .family = AF_MSM_IPC,
614 .bind = msm_ipc_router_bind,
615 .connect = sock_no_connect,
616 .sendmsg = msm_ipc_router_sendmsg,
617 .recvmsg = msm_ipc_router_recvmsg,
618 .ioctl = msm_ipc_router_ioctl,
619 .poll = msm_ipc_router_poll,
620 .setsockopt = sock_no_setsockopt,
621 .getsockopt = sock_no_getsockopt,
622 .release = msm_ipc_router_close,
623};
624
625static struct proto msm_ipc_proto = {
626 .name = "MSM_IPC",
627 .owner = THIS_MODULE,
628 .obj_size = sizeof(struct msm_ipc_sock),
629};
630
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530631/**
632 * msm_ipc_router_ipc_log_init() - Init function for IPC Logging
633 *
634 * Initialize the buffers to be used to provide the log information
635 * pertaining to the request, response and indication data flow that
636 * happens between user and kernel spaces.
637 */
638void msm_ipc_router_ipc_log_init(void)
639{
640 ipc_req_resp_log_txt =
Zaheerulla Meercbd869b2013-03-08 17:31:23 +0530641 ipc_log_context_create(REQ_RESP_IPC_LOG_PAGES,
642 "ipc_rtr_req_resp");
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530643 if (!ipc_req_resp_log_txt) {
644 pr_err("%s: Unable to create IPC logging for Req/Resp",
645 __func__);
646 }
647 ipc_ind_log_txt =
Zaheerulla Meercbd869b2013-03-08 17:31:23 +0530648 ipc_log_context_create(IND_IPC_LOG_PAGES, "ipc_rtr_ind");
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530649 if (!ipc_ind_log_txt) {
650 pr_err("%s: Unable to create IPC logging for Indications",
651 __func__);
652 }
653}
654
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655int msm_ipc_router_init_sockets(void)
656{
657 int ret;
658
659 ret = proto_register(&msm_ipc_proto, 1);
660 if (ret) {
661 pr_err("Failed to register MSM_IPC protocol type\n");
662 goto out_init_sockets;
663 }
664
665 ret = sock_register(&msm_ipc_family_ops);
666 if (ret) {
667 pr_err("Failed to register MSM_IPC socket type\n");
668 proto_unregister(&msm_ipc_proto);
669 goto out_init_sockets;
670 }
671
672 sockets_enabled = 1;
Zaheerulla Meerf0707d52013-02-21 17:56:18 +0530673 msm_ipc_router_ipc_log_init();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674out_init_sockets:
675 return ret;
676}
677
678void msm_ipc_router_exit_sockets(void)
679{
680 if (!sockets_enabled)
681 return;
682
683 sockets_enabled = 0;
684 sock_unregister(msm_ipc_family_ops.family);
685 proto_unregister(&msm_ipc_proto);
686}