summaryrefslogtreecommitdiff
path: root/net/mhi/mhi_raw.c
diff options
context:
space:
mode:
authorRaj Jayaraman <rjayaraman@nvidia.com>2012-09-16 16:10:37 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:41:47 -0700
commit54c1fa446b992b4325e9e773c403ac2e947e5b9e (patch)
tree2b111250259ea2a03c0219b202762ee0e2d0e707 /net/mhi/mhi_raw.c
parent67c2382bc78537257d837ea57eb2ec95675480e5 (diff)
net: Add MHI support for RMC PegaPCI.
* As submitted by RMC for modem support * Bug 1054808 Change-Id: I37f027eaed75bddfdb4cec7dd03501f6749634e9 Signed-off-by: Raj Jayaraman <rjayaraman@nvidia.com> Reviewed-on: http://git-master/r/160033 (cherry picked from commit 29bed237b4d4f7956f839411777d3855674d4bde) Reviewed-on: http://git-master/r/162293 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: WK Tsai <wtsai@nvidia.com> Reviewed-by: Steve Lin <stlin@nvidia.com>
Diffstat (limited to 'net/mhi/mhi_raw.c')
-rw-r--r--net/mhi/mhi_raw.c326
1 files changed, 326 insertions, 0 deletions
diff --git a/net/mhi/mhi_raw.c b/net/mhi/mhi_raw.c
new file mode 100644
index 000000000000..e15216727e49
--- /dev/null
+++ b/net/mhi/mhi_raw.c
@@ -0,0 +1,326 @@
+/*
+ * File: mhi_raw.c
+ *
+ * Copyright (C) 2011 Renesas Mobile Corporation. All rights reserved.
+ *
+ * Author: Petri Mattila <petri.to.mattila@renesasmobile.com>
+ *
+ * RAW socket implementation for MHI protocol family.
+ *
+ * It uses the MHI socket framework in mhi_socket.c
+ *
+ * This implementation is the most basic frame passing interface.
+ * The user space can use the sendmsg() and recvmsg() system calls
+ * to access the frames. The socket is created with the socket()
+ * system call, e.g. socket(PF_MHI,SOCK_RAW,l2proto).
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/mhi.h>
+#include <linux/l2mux.h>
+
+#include <asm/ioctls.h>
+
+#include <net/af_mhi.h>
+#include <net/mhi/sock.h>
+#include <net/mhi/raw.h>
+
+#ifdef CONFIG_MHI_DEBUG
+# define DPRINTK(...) printk(KERN_DEBUG "MHI/RAW: " __VA_ARGS__)
+#else
+# define DPRINTK(...)
+#endif
+
+
+/*** Prototypes ***/
+
+static struct proto mhi_raw_proto;
+
+static void mhi_raw_destruct(struct sock *sk);
+
+
+/*** Functions ***/
+
+int mhi_raw_sock_create(
+ struct net *net,
+ struct socket *sock,
+ int proto,
+ int kern)
+{
+ struct sock *sk;
+ struct mhi_sock *msk;
+
+ DPRINTK("mhi_raw_sock_create: proto:%d type:%d\n",
+ proto, sock->type);
+
+ if (sock->type != SOCK_RAW)
+ return -EPROTONOSUPPORT;
+
+ sk = sk_alloc(net, PF_MHI, GFP_KERNEL, &mhi_raw_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+
+ sock->ops = &mhi_socket_ops;
+ sock->state = SS_UNCONNECTED;
+
+ if (proto != MHI_L3_ANY)
+ sk->sk_protocol = proto;
+ else
+ sk->sk_protocol = 0;
+
+ sk->sk_destruct = mhi_raw_destruct;
+ sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
+
+ sk->sk_prot->init(sk);
+
+ msk = mhi_sk(sk);
+
+ msk->sk_l3proto = proto;
+ msk->sk_ifindex = -1;
+
+ return 0;
+}
+
+static int mhi_raw_init(struct sock *sk)
+{
+ return 0;
+}
+
+static void mhi_raw_destruct(struct sock *sk)
+{
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static void mhi_raw_close(struct sock *sk, long timeout)
+{
+ sk_common_release(sk);
+}
+
+static int mhi_raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ int err;
+
+ DPRINTK("mhi_raw_ioctl: cmd:%d arg:%lu\n", cmd, arg);
+
+ switch (cmd) {
+ case SIOCOUTQ:
+ {
+ int len;
+ len = sk_wmem_alloc_get(sk);
+ err = put_user(len, (int __user *)arg);
+ }
+ break;
+
+ case SIOCINQ:
+ {
+ struct sk_buff *skb;
+ int len;
+
+ lock_sock(sk);
+ {
+ skb = skb_peek(&sk->sk_receive_queue);
+ len = skb ? skb->len : 0;
+ }
+ release_sock(sk);
+
+ err = put_user(len, (int __user *)arg);
+ }
+ break;
+
+ default:
+ err = -ENOIOCTLCMD;
+ }
+
+ return err;
+}
+
+static int mhi_raw_sendmsg(
+ struct kiocb *iocb,
+ struct sock *sk,
+ struct msghdr *msg,
+ size_t len)
+{
+ struct mhi_sock *msk = mhi_sk(sk);
+ struct net_device *dev = NULL;
+ struct sk_buff *skb;
+
+ int err = -EFAULT;
+
+ if (msg->msg_flags &
+ ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|MSG_CMSG_COMPAT)) {
+ printk(KERN_WARNING
+ "mhi_raw_sendmsg: incompatible socket msg_flags: 0x%08X\n",
+ msg->msg_flags);
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ skb = sock_alloc_send_skb(sk,
+ len,
+ (msg->msg_flags & MSG_DONTWAIT),
+ &err);
+ if (!skb) {
+ printk(KERN_ERR
+ "mhi_raw_sendmsg: sock_alloc_send_skb failed: %d\n",
+ err);
+ goto out;
+ }
+
+ err = memcpy_fromiovec((void *)skb_put(skb, len), msg->msg_iov, len);
+ if (err < 0) {
+ printk(KERN_ERR
+ "mhi_raw_sendmsg: memcpy_fromiovec failed: %d\n",
+ err);
+ goto drop;
+ }
+
+ if (msk->sk_ifindex)
+ dev = dev_get_by_index(sock_net(sk), msk->sk_ifindex);
+
+ if (!dev) {
+ printk(KERN_ERR
+ "mhi_raw_sendmsg: no device for ifindex:%d\n",
+ msk->sk_ifindex);
+ goto drop;
+ }
+
+ if (!(dev->flags & IFF_UP)) {
+ printk(KERN_ERR
+ "mhi_raw_sendmsg: device %d not IFF_UP\n",
+ msk->sk_ifindex);
+ err = -ENETDOWN;
+ goto drop;
+ }
+
+ if (len > dev->mtu) {
+ err = -EMSGSIZE;
+ goto drop;
+ }
+
+ skb_reset_network_header(skb);
+ skb_reset_mac_header(skb);
+
+ err = mhi_skb_send(skb, dev, sk->sk_protocol);
+
+ goto put;
+
+drop:
+ kfree(skb);
+put:
+ if (dev)
+ dev_put(dev);
+out:
+ return err;
+}
+
+static int mhi_raw_recvmsg(
+ struct kiocb *iocb,
+ struct sock *sk,
+ struct msghdr *msg,
+ size_t len,
+ int noblock,
+ int flags,
+ int *addr_len)
+{
+ struct sk_buff *skb = NULL;
+ int cnt, err;
+
+ err = -EOPNOTSUPP;
+
+ if (flags &
+ ~(MSG_PEEK|MSG_TRUNC|MSG_DONTWAIT|
+ MSG_NOSIGNAL|MSG_CMSG_COMPAT)) {
+ printk(KERN_WARNING
+ "mhi_raw_recvmsg: incompatible socket flags: 0x%08X",
+ flags);
+ goto out2;
+ }
+
+ if (addr_len)
+ addr_len[0] = 0;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ if (!skb)
+ goto out2;
+
+ cnt = skb->len;
+ if (len < cnt) {
+ msg->msg_flags |= MSG_TRUNC;
+ cnt = len;
+ }
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, cnt);
+ if (err)
+ goto out;
+
+ if (flags & MSG_TRUNC)
+ err = skb->len;
+ else
+ err = cnt;
+
+out:
+ skb_free_datagram(sk, skb);
+out2:
+ return err;
+}
+
+static int mhi_raw_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ if (sock_queue_rcv_skb(sk, skb) < 0) {
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
+
+ return NET_RX_SUCCESS;
+}
+
+
+static struct proto mhi_raw_proto = {
+ .name = "MHI-RAW",
+ .owner = THIS_MODULE,
+ .close = mhi_raw_close,
+ .ioctl = mhi_raw_ioctl,
+ .init = mhi_raw_init,
+ .sendmsg = mhi_raw_sendmsg,
+ .recvmsg = mhi_raw_recvmsg,
+ .backlog_rcv = mhi_raw_backlog_rcv,
+ .hash = mhi_sock_hash,
+ .unhash = mhi_sock_unhash,
+ .obj_size = sizeof(struct mhi_sock),
+};
+
+
+int mhi_raw_proto_init(void)
+{
+ DPRINTK("mhi_raw_proto_init\n");
+
+ return proto_register(&mhi_raw_proto, 1);
+}
+
+void mhi_raw_proto_exit(void)
+{
+ DPRINTK("mhi_raw_proto_exit\n");
+
+ proto_unregister(&mhi_raw_proto);
+}
+