Phonet: common socket glue
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / phonet / af_phonet.c
1 /*
2 * File: af_phonet.c
3 *
4 * Phonet protocols family
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/module.h>
28 #include <asm/unaligned.h>
29 #include <net/sock.h>
30
31 #include <linux/if_phonet.h>
32 #include <linux/phonet.h>
33 #include <net/phonet/phonet.h>
34 #include <net/phonet/pn_dev.h>
35
36 static struct net_proto_family phonet_proto_family;
37 static struct phonet_protocol *phonet_proto_get(int protocol);
38 static inline void phonet_proto_put(struct phonet_protocol *pp);
39
40 /* protocol family functions */
41
42 static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
43 {
44 struct sock *sk;
45 struct pn_sock *pn;
46 struct phonet_protocol *pnp;
47 int err;
48
49 if (net != &init_net)
50 return -EAFNOSUPPORT;
51
52 if (!capable(CAP_SYS_ADMIN))
53 return -EPERM;
54
55 if (protocol == 0) {
56 /* Default protocol selection */
57 switch (sock->type) {
58 case SOCK_DGRAM:
59 protocol = PN_PROTO_PHONET;
60 break;
61 default:
62 return -EPROTONOSUPPORT;
63 }
64 }
65
66 pnp = phonet_proto_get(protocol);
67 if (pnp == NULL)
68 return -EPROTONOSUPPORT;
69 if (sock->type != pnp->sock_type) {
70 err = -EPROTONOSUPPORT;
71 goto out;
72 }
73
74 sk = sk_alloc(net, PF_PHONET, GFP_KERNEL, pnp->prot);
75 if (sk == NULL) {
76 err = -ENOMEM;
77 goto out;
78 }
79
80 sock_init_data(sock, sk);
81 sock->state = SS_UNCONNECTED;
82 sock->ops = pnp->ops;
83 sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
84 sk->sk_protocol = protocol;
85 pn = pn_sk(sk);
86 pn->sobject = 0;
87 pn->resource = 0;
88 sk->sk_prot->init(sk);
89 err = 0;
90
91 out:
92 phonet_proto_put(pnp);
93 return err;
94 }
95
96 static struct net_proto_family phonet_proto_family = {
97 .family = AF_PHONET,
98 .create = pn_socket_create,
99 .owner = THIS_MODULE,
100 };
101
102 /* packet type functions */
103
104 /*
105 * Stuff received packets to associated sockets.
106 * On error, returns non-zero and releases the skb.
107 */
108 static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
109 struct packet_type *pkttype,
110 struct net_device *orig_dev)
111 {
112 struct phonethdr *ph;
113 struct sock *sk;
114 struct sockaddr_pn sa;
115 u16 len;
116
117 if (dev_net(dev) != &init_net)
118 goto out;
119
120 /* check we have at least a full Phonet header */
121 if (!pskb_pull(skb, sizeof(struct phonethdr)))
122 goto out;
123
124 /* check that the advertised length is correct */
125 ph = pn_hdr(skb);
126 len = get_unaligned_be16(&ph->pn_length);
127 if (len < 2)
128 goto out;
129 len -= 2;
130 if ((len > skb->len) || pskb_trim(skb, len))
131 goto out;
132 skb_reset_transport_header(skb);
133
134 pn_skb_get_dst_sockaddr(skb, &sa);
135 if (pn_sockaddr_get_addr(&sa) == 0)
136 goto out; /* currently, we cannot be device 0 */
137
138 sk = pn_find_sock_by_sa(&sa);
139 if (sk == NULL)
140 goto out;
141
142 /* Push data to the socket (or other sockets connected to it). */
143 return sk_receive_skb(sk, skb, 0);
144
145 out:
146 kfree_skb(skb);
147 return NET_RX_DROP;
148 }
149
150 static struct packet_type phonet_packet_type = {
151 .type = __constant_htons(ETH_P_PHONET),
152 .dev = NULL,
153 .func = phonet_rcv,
154 };
155
156 /* Transport protocol registration */
157 static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly;
158 static DEFINE_SPINLOCK(proto_tab_lock);
159
160 int __init_or_module phonet_proto_register(int protocol,
161 struct phonet_protocol *pp)
162 {
163 int err = 0;
164
165 if (protocol >= PHONET_NPROTO)
166 return -EINVAL;
167
168 err = proto_register(pp->prot, 1);
169 if (err)
170 return err;
171
172 spin_lock(&proto_tab_lock);
173 if (proto_tab[protocol])
174 err = -EBUSY;
175 else
176 proto_tab[protocol] = pp;
177 spin_unlock(&proto_tab_lock);
178
179 return err;
180 }
181 EXPORT_SYMBOL(phonet_proto_register);
182
183 void phonet_proto_unregister(int protocol, struct phonet_protocol *pp)
184 {
185 spin_lock(&proto_tab_lock);
186 BUG_ON(proto_tab[protocol] != pp);
187 proto_tab[protocol] = NULL;
188 spin_unlock(&proto_tab_lock);
189 proto_unregister(pp->prot);
190 }
191 EXPORT_SYMBOL(phonet_proto_unregister);
192
193 static struct phonet_protocol *phonet_proto_get(int protocol)
194 {
195 struct phonet_protocol *pp;
196
197 if (protocol >= PHONET_NPROTO)
198 return NULL;
199
200 spin_lock(&proto_tab_lock);
201 pp = proto_tab[protocol];
202 if (pp && !try_module_get(pp->prot->owner))
203 pp = NULL;
204 spin_unlock(&proto_tab_lock);
205
206 return pp;
207 }
208
209 static inline void phonet_proto_put(struct phonet_protocol *pp)
210 {
211 module_put(pp->prot->owner);
212 }
213
214 /* Module registration */
215 static int __init phonet_init(void)
216 {
217 int err;
218
219 err = sock_register(&phonet_proto_family);
220 if (err) {
221 printk(KERN_ALERT
222 "phonet protocol family initialization failed\n");
223 return err;
224 }
225
226 phonet_device_init();
227 dev_add_pack(&phonet_packet_type);
228 phonet_netlink_register();
229 return 0;
230 }
231
232 static void __exit phonet_exit(void)
233 {
234 sock_unregister(AF_PHONET);
235 dev_remove_pack(&phonet_packet_type);
236 phonet_device_exit();
237 }
238
239 module_init(phonet_init);
240 module_exit(phonet_exit);
241 MODULE_DESCRIPTION("Phonet protocol stack for Linux");
242 MODULE_LICENSE("GPL");