blob: 4b0d26a6ac1bf74bef814c186a2bf4b079e8f0ef [file] [log] [blame]
Karthikeyan Ramasubramanian1df33cc2012-01-30 14:54:17 -07001/* Copyright (c) 2011-2012, Code Aurora Forum. 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>
23
24#include <asm/string.h>
25#include <asm/atomic.h>
26
27#include <net/sock.h>
28
29#include <mach/peripheral-loader.h>
Karthikeyan Ramasubramanian1df33cc2012-01-30 14:54:17 -070030#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070031
32#include "ipc_router.h"
33
34#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
35#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
36#define MODEM_LOAD_TIMEOUT (10 * HZ)
37
38static int sockets_enabled;
39static struct proto msm_ipc_proto;
40static const struct proto_ops msm_ipc_proto_ops;
41
42static void msm_ipc_router_unload_modem(void *pil)
43{
44 if (pil)
45 pil_put(pil);
46}
47
48static void *msm_ipc_router_load_modem(void)
49{
Karthikeyan Ramasubramanian1df33cc2012-01-30 14:54:17 -070050 void *pil = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051 int rc;
52
Karthikeyan Ramasubramanian1df33cc2012-01-30 14:54:17 -070053 /* Load GNSS for Standalone 8064 but not for Fusion 3 */
54 if (cpu_is_apq8064()) {
55 if (socinfo_get_platform_subtype() == 0x0)
56 pil = pil_get("gnss");
57 } else {
58 pil = pil_get("modem");
59 }
60
61 if (IS_ERR(pil) || !pil) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062 pr_debug("%s: modem load failed\n", __func__);
63 pil = NULL;
64 } else {
65 rc = wait_for_completion_interruptible_timeout(
66 &msm_ipc_remote_router_up,
67 MODEM_LOAD_TIMEOUT);
68 if (!rc)
69 rc = -ETIMEDOUT;
70 if (rc < 0) {
71 pr_err("%s: wait for remote router failed %d\n",
72 __func__, rc);
73 msm_ipc_router_unload_modem(pil);
74 pil = NULL;
75 }
76 }
77
78 return pil;
79}
80
81static struct sk_buff_head *msm_ipc_router_build_msg(unsigned int num_sect,
82 struct iovec const *msg_sect,
83 size_t total_len)
84{
85 struct sk_buff_head *msg_head;
86 struct sk_buff *msg;
87 int i, copied, first = 1;
88 int data_size = 0, request_size, offset;
89 void *data;
90
91 for (i = 0; i < num_sect; i++)
92 data_size += msg_sect[i].iov_len;
93
94 if (!data_size)
95 return NULL;
96
97 msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
98 if (!msg_head) {
99 pr_err("%s: cannot allocate skb_head\n", __func__);
100 return NULL;
101 }
102 skb_queue_head_init(msg_head);
103
104 for (copied = 1, i = 0; copied && (i < num_sect); i++) {
105 data_size = msg_sect[i].iov_len;
106 offset = 0;
107 while (offset != msg_sect[i].iov_len) {
108 request_size = data_size;
109 if (first)
110 request_size += IPC_ROUTER_HDR_SIZE;
111
112 msg = alloc_skb(request_size, GFP_KERNEL);
113 if (!msg) {
114 if (request_size <= (PAGE_SIZE/2)) {
115 pr_err("%s: cannot allocated skb\n",
116 __func__);
117 goto msg_build_failure;
118 }
119 data_size = data_size / 2;
120 continue;
121 }
122
123 if (first) {
124 skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
125 first = 0;
126 }
127
128 data = skb_put(msg, data_size);
129 copied = !copy_from_user(msg->data,
130 msg_sect[i].iov_base + offset,
131 data_size);
132 if (!copied) {
133 pr_err("%s: copy_from_user failed\n",
134 __func__);
135 kfree_skb(msg);
136 goto msg_build_failure;
137 }
138 skb_queue_tail(msg_head, msg);
139 offset += data_size;
140 data_size = msg_sect[i].iov_len - offset;
141 }
142 }
143 return msg_head;
144
145msg_build_failure:
146 while (!skb_queue_empty(msg_head)) {
147 msg = skb_dequeue(msg_head);
148 kfree_skb(msg);
149 }
150 kfree(msg_head);
151 return NULL;
152}
153
154static int msm_ipc_router_extract_msg(struct msghdr *m,
155 struct sk_buff_head *msg_head)
156{
157 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)m->msg_name;
158 struct rr_header *hdr;
159 struct sk_buff *temp;
160 int offset = 0, data_len = 0, copy_len;
161
162 if (!m || !msg_head) {
163 pr_err("%s: Invalid pointers passed\n", __func__);
164 return -EINVAL;
165 }
166
167 temp = skb_peek(msg_head);
168 hdr = (struct rr_header *)(temp->data);
169 if (addr || (hdr->src_port_id != IPC_ROUTER_ADDRESS)) {
170 addr->family = AF_MSM_IPC;
171 addr->address.addrtype = MSM_IPC_ADDR_ID;
172 addr->address.addr.port_addr.node_id = hdr->src_node_id;
173 addr->address.addr.port_addr.port_id = hdr->src_port_id;
174 m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
175 }
176
177 data_len = hdr->size;
178 skb_pull(temp, IPC_ROUTER_HDR_SIZE);
179 skb_queue_walk(msg_head, temp) {
180 copy_len = data_len < temp->len ? data_len : temp->len;
181 if (copy_to_user(m->msg_iov->iov_base + offset, temp->data,
182 copy_len)) {
183 pr_err("%s: Copy to user failed\n", __func__);
184 return -EFAULT;
185 }
186 offset += copy_len;
187 data_len -= copy_len;
188 }
189 return offset;
190}
191
192static void msm_ipc_router_release_msg(struct sk_buff_head *msg_head)
193{
194 struct sk_buff *temp;
195
196 if (!msg_head) {
197 pr_err("%s: Invalid msg pointer\n", __func__);
198 return;
199 }
200
201 while (!skb_queue_empty(msg_head)) {
202 temp = skb_dequeue(msg_head);
203 kfree_skb(temp);
204 }
205 kfree(msg_head);
206}
207
208static int msm_ipc_router_create(struct net *net,
209 struct socket *sock,
210 int protocol,
211 int kern)
212{
213 struct sock *sk;
214 struct msm_ipc_port *port_ptr;
215 void *pil;
216
217 if (unlikely(protocol != 0)) {
218 pr_err("%s: Protocol not supported\n", __func__);
219 return -EPROTONOSUPPORT;
220 }
221
222 switch (sock->type) {
223 case SOCK_DGRAM:
224 break;
225 default:
226 pr_err("%s: Protocol type not supported\n", __func__);
227 return -EPROTOTYPE;
228 }
229
230 sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto);
231 if (!sk) {
232 pr_err("%s: sk_alloc failed\n", __func__);
233 return -ENOMEM;
234 }
235
236 port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
237 if (!port_ptr) {
238 pr_err("%s: port_ptr alloc failed\n", __func__);
239 sk_free(sk);
240 return -ENOMEM;
241 }
242
243 sock->ops = &msm_ipc_proto_ops;
244 sock_init_data(sock, sk);
245 sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
246
247 pil = msm_ipc_router_load_modem();
248 msm_ipc_sk(sk)->port = port_ptr;
249 msm_ipc_sk(sk)->modem_pil = pil;
250
251 return 0;
252}
253
254int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
255 int uaddr_len)
256{
257 struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
258 struct sock *sk = sock->sk;
259 struct msm_ipc_port *port_ptr;
260 int ret;
261
262 if (!sk)
263 return -EINVAL;
264
265 if (!uaddr_len) {
266 pr_err("%s: Invalid address length\n", __func__);
267 return -EINVAL;
268 }
269
270 if (addr->family != AF_MSM_IPC) {
271 pr_err("%s: Address family is incorrect\n", __func__);
272 return -EAFNOSUPPORT;
273 }
274
275 if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
276 pr_err("%s: Address type is incorrect\n", __func__);
277 return -EINVAL;
278 }
279
280 port_ptr = msm_ipc_sk_port(sk);
281 if (!port_ptr)
282 return -ENODEV;
283
284 lock_sock(sk);
285
286 ret = msm_ipc_router_register_server(port_ptr, &addr->address);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700287
288 release_sock(sk);
289 return ret;
290}
291
292static int msm_ipc_router_sendmsg(struct kiocb *iocb, struct socket *sock,
293 struct msghdr *m, size_t total_len)
294{
295 struct sock *sk = sock->sk;
296 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
297 struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
298 struct sk_buff_head *msg;
299 int ret;
300
301 if (!dest)
302 return -EDESTADDRREQ;
303
304 if (m->msg_namelen < sizeof(*dest) || dest->family != AF_MSM_IPC)
305 return -EINVAL;
306
307 if (total_len > MAX_IPC_PKT_SIZE)
308 return -EINVAL;
309
310 lock_sock(sk);
311 msg = msm_ipc_router_build_msg(m->msg_iovlen, m->msg_iov, total_len);
312 if (!msg) {
313 pr_err("%s: Msg build failure\n", __func__);
314 ret = -ENOMEM;
315 goto out_sendmsg;
316 }
317
318 ret = msm_ipc_router_send_to(port_ptr, msg, &dest->address);
319 if (ret == (IPC_ROUTER_HDR_SIZE + total_len))
320 ret = total_len;
321
322out_sendmsg:
323 release_sock(sk);
324 return ret;
325}
326
327static int msm_ipc_router_recvmsg(struct kiocb *iocb, struct socket *sock,
328 struct msghdr *m, size_t buf_len, int flags)
329{
330 struct sock *sk = sock->sk;
331 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
332 struct sk_buff_head *msg;
333 long timeout;
334 int ret;
335
336 if (m->msg_iovlen != 1)
337 return -EOPNOTSUPP;
338
339 if (!buf_len)
340 return -EINVAL;
341
342 lock_sock(sk);
343 timeout = sk->sk_rcvtimeo;
344 mutex_lock(&port_ptr->port_rx_q_lock);
345 while (list_empty(&port_ptr->port_rx_q)) {
346 mutex_unlock(&port_ptr->port_rx_q_lock);
347 release_sock(sk);
348 if (timeout < 0) {
349 ret = wait_event_interruptible(
350 port_ptr->port_rx_wait_q,
351 !list_empty(&port_ptr->port_rx_q));
352 if (ret)
353 return ret;
354 } else if (timeout > 0) {
355 timeout = wait_event_interruptible_timeout(
356 port_ptr->port_rx_wait_q,
357 !list_empty(&port_ptr->port_rx_q),
358 timeout);
359 if (timeout < 0)
360 return -EFAULT;
361 }
362
363 if (timeout == 0)
364 return -ETIMEDOUT;
365 lock_sock(sk);
366 mutex_lock(&port_ptr->port_rx_q_lock);
367 }
368 mutex_unlock(&port_ptr->port_rx_q_lock);
369
370 ret = msm_ipc_router_read(port_ptr, &msg, buf_len);
371 if (ret <= 0 || !msg) {
372 release_sock(sk);
373 return ret;
374 }
375
376 ret = msm_ipc_router_extract_msg(m, msg);
377 msm_ipc_router_release_msg(msg);
378 msg = NULL;
379 release_sock(sk);
380 return ret;
381}
382
383static int msm_ipc_router_ioctl(struct socket *sock,
384 unsigned int cmd, unsigned long arg)
385{
386 struct sock *sk = sock->sk;
387 struct msm_ipc_port *port_ptr;
388 struct server_lookup_args server_arg;
389 struct msm_ipc_port_addr *port_addr = NULL;
390 unsigned int n, port_addr_sz = 0;
391 int ret;
392
393 if (!sk)
394 return -EINVAL;
395
396 lock_sock(sk);
397 port_ptr = msm_ipc_sk_port(sock->sk);
398 if (!port_ptr) {
399 release_sock(sk);
400 return -EINVAL;
401 }
402
403 switch (cmd) {
404 case IPC_ROUTER_IOCTL_GET_VERSION:
405 n = IPC_ROUTER_VERSION;
406 ret = put_user(n, (unsigned int *)arg);
407 break;
408
409 case IPC_ROUTER_IOCTL_GET_MTU:
410 n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
411 ret = put_user(n, (unsigned int *)arg);
412 break;
413
414 case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
415 ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
416 break;
417
418 case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
419 ret = copy_from_user(&server_arg, (void *)arg,
420 sizeof(server_arg));
421 if (ret) {
422 ret = -EFAULT;
423 break;
424 }
425
426 if (server_arg.num_entries_in_array < 0) {
427 ret = -EINVAL;
428 break;
429 }
430 if (server_arg.num_entries_in_array) {
431 port_addr_sz = server_arg.num_entries_in_array *
432 sizeof(*port_addr);
433 port_addr = kmalloc(port_addr_sz, GFP_KERNEL);
434 if (!port_addr) {
435 ret = -ENOMEM;
436 break;
437 }
438 }
439 ret = msm_ipc_router_lookup_server_name(&server_arg.port_name,
Karthikeyan Ramasubramaniancc450c92011-07-27 14:38:15 -0600440 port_addr, server_arg.num_entries_in_array,
441 server_arg.lookup_mask);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 if (ret < 0) {
443 pr_err("%s: Server not found\n", __func__);
444 ret = -ENODEV;
445 kfree(port_addr);
446 break;
447 }
448 server_arg.num_entries_found = ret;
449
450 ret = copy_to_user((void *)arg, &server_arg,
451 sizeof(server_arg));
452 if (port_addr_sz) {
453 ret = copy_to_user((void *)(arg + sizeof(server_arg)),
454 port_addr, port_addr_sz);
455 if (ret)
456 ret = -EFAULT;
457 kfree(port_addr);
458 }
459 break;
460
461 case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
462 ret = msm_ipc_router_bind_control_port(port_ptr);
463 break;
464
465 default:
466 ret = -EINVAL;
467 }
468 release_sock(sk);
469 return ret;
470}
471
472static unsigned int msm_ipc_router_poll(struct file *file,
473 struct socket *sock, poll_table *wait)
474{
475 struct sock *sk = sock->sk;
476 struct msm_ipc_port *port_ptr;
477 uint32_t mask = 0;
478
479 if (!sk)
480 return -EINVAL;
481
482 port_ptr = msm_ipc_sk_port(sk);
483 if (!port_ptr)
484 return -EINVAL;
485
486 poll_wait(file, &port_ptr->port_rx_wait_q, wait);
487
488 if (!list_empty(&port_ptr->port_rx_q))
489 mask |= (POLLRDNORM | POLLIN);
490
491 return mask;
492}
493
494static int msm_ipc_router_close(struct socket *sock)
495{
496 struct sock *sk = sock->sk;
497 struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
498 void *pil = msm_ipc_sk(sk)->modem_pil;
499 int ret;
500
501 lock_sock(sk);
502 ret = msm_ipc_router_close_port(port_ptr);
503 msm_ipc_router_unload_modem(pil);
504 release_sock(sk);
505 sock_put(sk);
506 sock->sk = NULL;
507
508 return ret;
509}
510
511static const struct net_proto_family msm_ipc_family_ops = {
512 .owner = THIS_MODULE,
513 .family = AF_MSM_IPC,
514 .create = msm_ipc_router_create
515};
516
517static const struct proto_ops msm_ipc_proto_ops = {
518 .owner = THIS_MODULE,
519 .family = AF_MSM_IPC,
520 .bind = msm_ipc_router_bind,
521 .connect = sock_no_connect,
522 .sendmsg = msm_ipc_router_sendmsg,
523 .recvmsg = msm_ipc_router_recvmsg,
524 .ioctl = msm_ipc_router_ioctl,
525 .poll = msm_ipc_router_poll,
526 .setsockopt = sock_no_setsockopt,
527 .getsockopt = sock_no_getsockopt,
528 .release = msm_ipc_router_close,
529};
530
531static struct proto msm_ipc_proto = {
532 .name = "MSM_IPC",
533 .owner = THIS_MODULE,
534 .obj_size = sizeof(struct msm_ipc_sock),
535};
536
537int msm_ipc_router_init_sockets(void)
538{
539 int ret;
540
541 ret = proto_register(&msm_ipc_proto, 1);
542 if (ret) {
543 pr_err("Failed to register MSM_IPC protocol type\n");
544 goto out_init_sockets;
545 }
546
547 ret = sock_register(&msm_ipc_family_ops);
548 if (ret) {
549 pr_err("Failed to register MSM_IPC socket type\n");
550 proto_unregister(&msm_ipc_proto);
551 goto out_init_sockets;
552 }
553
554 sockets_enabled = 1;
555out_init_sockets:
556 return ret;
557}
558
559void msm_ipc_router_exit_sockets(void)
560{
561 if (!sockets_enabled)
562 return;
563
564 sockets_enabled = 0;
565 sock_unregister(msm_ipc_family_ops.family);
566 proto_unregister(&msm_ipc_proto);
567}