xref: /openbmc/linux/net/phonet/datagram.c (revision ecba1060)
1 /*
2  * File: datagram.c
3  *
4  * Datagram (ISI) Phonet sockets
5  *
6  * Copyright (C) 2008 Nokia Corporation.
7  *
8  * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com>
9  * Original author: Sakari Ailus <sakari.ailus@nokia.com>
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * version 2 as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25 
26 #include <linux/kernel.h>
27 #include <linux/socket.h>
28 #include <asm/ioctls.h>
29 #include <net/sock.h>
30 
31 #include <linux/phonet.h>
32 #include <net/phonet/phonet.h>
33 
34 static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb);
35 
36 /* associated socket ceases to exist */
37 static void pn_sock_close(struct sock *sk, long timeout)
38 {
39 	sk_common_release(sk);
40 }
41 
42 static int pn_ioctl(struct sock *sk, int cmd, unsigned long arg)
43 {
44 	struct sk_buff *skb;
45 	int answ;
46 
47 	switch (cmd) {
48 	case SIOCINQ:
49 		lock_sock(sk);
50 		skb = skb_peek(&sk->sk_receive_queue);
51 		answ = skb ? skb->len : 0;
52 		release_sock(sk);
53 		return put_user(answ, (int __user *)arg);
54 	}
55 
56 	return -ENOIOCTLCMD;
57 }
58 
59 /* Destroy socket. All references are gone. */
60 static void pn_destruct(struct sock *sk)
61 {
62 	skb_queue_purge(&sk->sk_receive_queue);
63 }
64 
65 static int pn_init(struct sock *sk)
66 {
67 	sk->sk_destruct = pn_destruct;
68 	return 0;
69 }
70 
71 static int pn_sendmsg(struct kiocb *iocb, struct sock *sk,
72 			struct msghdr *msg, size_t len)
73 {
74 	struct sockaddr_pn *target;
75 	struct sk_buff *skb;
76 	int err;
77 
78 	if (msg->msg_flags & MSG_OOB)
79 		return -EOPNOTSUPP;
80 
81 	if (msg->msg_name == NULL)
82 		return -EDESTADDRREQ;
83 
84 	if (msg->msg_namelen < sizeof(struct sockaddr_pn))
85 		return -EINVAL;
86 
87 	target = (struct sockaddr_pn *)msg->msg_name;
88 	if (target->spn_family != AF_PHONET)
89 		return -EAFNOSUPPORT;
90 
91 	skb = sock_alloc_send_skb(sk, MAX_PHONET_HEADER + len,
92 					msg->msg_flags & MSG_DONTWAIT, &err);
93 	if (skb == NULL)
94 		return err;
95 	skb_reserve(skb, MAX_PHONET_HEADER);
96 
97 	err = memcpy_fromiovec((void *)skb_put(skb, len), msg->msg_iov, len);
98 	if (err < 0) {
99 		kfree_skb(skb);
100 		return err;
101 	}
102 
103 	/*
104 	 * Fill in the Phonet header and
105 	 * finally pass the packet forwards.
106 	 */
107 	err = pn_skb_send(sk, skb, target);
108 
109 	/* If ok, return len. */
110 	return (err >= 0) ? len : err;
111 }
112 
113 static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
114 			struct msghdr *msg, size_t len, int noblock,
115 			int flags, int *addr_len)
116 {
117 	struct sk_buff *skb = NULL;
118 	struct sockaddr_pn sa;
119 	int rval = -EOPNOTSUPP;
120 	int copylen;
121 
122 	if (flags & MSG_OOB)
123 		goto out_nofree;
124 
125 	if (addr_len)
126 		*addr_len = sizeof(sa);
127 
128 	skb = skb_recv_datagram(sk, flags, noblock, &rval);
129 	if (skb == NULL)
130 		goto out_nofree;
131 
132 	pn_skb_get_src_sockaddr(skb, &sa);
133 
134 	copylen = skb->len;
135 	if (len < copylen) {
136 		msg->msg_flags |= MSG_TRUNC;
137 		copylen = len;
138 	}
139 
140 	rval = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copylen);
141 	if (rval) {
142 		rval = -EFAULT;
143 		goto out;
144 	}
145 
146 	rval = (flags & MSG_TRUNC) ? skb->len : copylen;
147 
148 	if (msg->msg_name != NULL)
149 		memcpy(msg->msg_name, &sa, sizeof(struct sockaddr_pn));
150 
151 out:
152 	skb_free_datagram(sk, skb);
153 
154 out_nofree:
155 	return rval;
156 }
157 
158 /* Queue an skb for a sock. */
159 static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb)
160 {
161 	int err = sock_queue_rcv_skb(sk, skb);
162 	if (err < 0)
163 		kfree_skb(skb);
164 	return err ? NET_RX_DROP : NET_RX_SUCCESS;
165 }
166 
167 /* Module registration */
168 static struct proto pn_proto = {
169 	.close		= pn_sock_close,
170 	.ioctl		= pn_ioctl,
171 	.init		= pn_init,
172 	.sendmsg	= pn_sendmsg,
173 	.recvmsg	= pn_recvmsg,
174 	.backlog_rcv	= pn_backlog_rcv,
175 	.hash		= pn_sock_hash,
176 	.unhash		= pn_sock_unhash,
177 	.get_port	= pn_sock_get_port,
178 	.obj_size	= sizeof(struct pn_sock),
179 	.owner		= THIS_MODULE,
180 	.name		= "PHONET",
181 };
182 
183 static struct phonet_protocol pn_dgram_proto = {
184 	.ops		= &phonet_dgram_ops,
185 	.prot		= &pn_proto,
186 	.sock_type	= SOCK_DGRAM,
187 };
188 
189 int __init isi_register(void)
190 {
191 	return phonet_proto_register(PN_PROTO_PHONET, &pn_dgram_proto);
192 }
193 
194 void __exit isi_unregister(void)
195 {
196 	phonet_proto_unregister(PN_PROTO_PHONET, &pn_dgram_proto);
197 }
198