xref: /openbmc/linux/net/phonet/af_phonet.c (revision 107d0d9b)
14b07b3f6SRemi Denis-Courmont /*
24b07b3f6SRemi Denis-Courmont  * File: af_phonet.c
34b07b3f6SRemi Denis-Courmont  *
44b07b3f6SRemi Denis-Courmont  * Phonet protocols family
54b07b3f6SRemi Denis-Courmont  *
64b07b3f6SRemi Denis-Courmont  * Copyright (C) 2008 Nokia Corporation.
74b07b3f6SRemi Denis-Courmont  *
84b07b3f6SRemi Denis-Courmont  * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com>
94b07b3f6SRemi Denis-Courmont  * Original author: Sakari Ailus <sakari.ailus@nokia.com>
104b07b3f6SRemi Denis-Courmont  *
114b07b3f6SRemi Denis-Courmont  * This program is free software; you can redistribute it and/or
124b07b3f6SRemi Denis-Courmont  * modify it under the terms of the GNU General Public License
134b07b3f6SRemi Denis-Courmont  * version 2 as published by the Free Software Foundation.
144b07b3f6SRemi Denis-Courmont  *
154b07b3f6SRemi Denis-Courmont  * This program is distributed in the hope that it will be useful, but
164b07b3f6SRemi Denis-Courmont  * WITHOUT ANY WARRANTY; without even the implied warranty of
174b07b3f6SRemi Denis-Courmont  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
184b07b3f6SRemi Denis-Courmont  * General Public License for more details.
194b07b3f6SRemi Denis-Courmont  *
204b07b3f6SRemi Denis-Courmont  * You should have received a copy of the GNU General Public License
214b07b3f6SRemi Denis-Courmont  * along with this program; if not, write to the Free Software
224b07b3f6SRemi Denis-Courmont  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
234b07b3f6SRemi Denis-Courmont  * 02110-1301 USA
244b07b3f6SRemi Denis-Courmont  */
254b07b3f6SRemi Denis-Courmont 
264b07b3f6SRemi Denis-Courmont #include <linux/kernel.h>
274b07b3f6SRemi Denis-Courmont #include <linux/module.h>
284b07b3f6SRemi Denis-Courmont #include <asm/unaligned.h>
294b07b3f6SRemi Denis-Courmont #include <net/sock.h>
304b07b3f6SRemi Denis-Courmont 
314b07b3f6SRemi Denis-Courmont #include <linux/if_phonet.h>
324b07b3f6SRemi Denis-Courmont #include <linux/phonet.h>
334b07b3f6SRemi Denis-Courmont #include <net/phonet/phonet.h>
34f8ff6028SRemi Denis-Courmont #include <net/phonet/pn_dev.h>
354b07b3f6SRemi Denis-Courmont 
364b07b3f6SRemi Denis-Courmont static struct net_proto_family phonet_proto_family;
374b07b3f6SRemi Denis-Courmont static struct phonet_protocol *phonet_proto_get(int protocol);
384b07b3f6SRemi Denis-Courmont static inline void phonet_proto_put(struct phonet_protocol *pp);
394b07b3f6SRemi Denis-Courmont 
404b07b3f6SRemi Denis-Courmont /* protocol family functions */
414b07b3f6SRemi Denis-Courmont 
424b07b3f6SRemi Denis-Courmont static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
434b07b3f6SRemi Denis-Courmont {
44ba113a94SRemi Denis-Courmont 	struct sock *sk;
45ba113a94SRemi Denis-Courmont 	struct pn_sock *pn;
464b07b3f6SRemi Denis-Courmont 	struct phonet_protocol *pnp;
474b07b3f6SRemi Denis-Courmont 	int err;
484b07b3f6SRemi Denis-Courmont 
494b07b3f6SRemi Denis-Courmont 	if (net != &init_net)
504b07b3f6SRemi Denis-Courmont 		return -EAFNOSUPPORT;
514b07b3f6SRemi Denis-Courmont 
524b07b3f6SRemi Denis-Courmont 	if (!capable(CAP_SYS_ADMIN))
534b07b3f6SRemi Denis-Courmont 		return -EPERM;
544b07b3f6SRemi Denis-Courmont 
554b07b3f6SRemi Denis-Courmont 	if (protocol == 0) {
564b07b3f6SRemi Denis-Courmont 		/* Default protocol selection */
574b07b3f6SRemi Denis-Courmont 		switch (sock->type) {
584b07b3f6SRemi Denis-Courmont 		case SOCK_DGRAM:
594b07b3f6SRemi Denis-Courmont 			protocol = PN_PROTO_PHONET;
604b07b3f6SRemi Denis-Courmont 			break;
614b07b3f6SRemi Denis-Courmont 		default:
624b07b3f6SRemi Denis-Courmont 			return -EPROTONOSUPPORT;
634b07b3f6SRemi Denis-Courmont 		}
644b07b3f6SRemi Denis-Courmont 	}
654b07b3f6SRemi Denis-Courmont 
664b07b3f6SRemi Denis-Courmont 	pnp = phonet_proto_get(protocol);
674b07b3f6SRemi Denis-Courmont 	if (pnp == NULL)
684b07b3f6SRemi Denis-Courmont 		return -EPROTONOSUPPORT;
694b07b3f6SRemi Denis-Courmont 	if (sock->type != pnp->sock_type) {
704b07b3f6SRemi Denis-Courmont 		err = -EPROTONOSUPPORT;
714b07b3f6SRemi Denis-Courmont 		goto out;
724b07b3f6SRemi Denis-Courmont 	}
734b07b3f6SRemi Denis-Courmont 
74ba113a94SRemi Denis-Courmont 	sk = sk_alloc(net, PF_PHONET, GFP_KERNEL, pnp->prot);
75ba113a94SRemi Denis-Courmont 	if (sk == NULL) {
76ba113a94SRemi Denis-Courmont 		err = -ENOMEM;
77ba113a94SRemi Denis-Courmont 		goto out;
78ba113a94SRemi Denis-Courmont 	}
79ba113a94SRemi Denis-Courmont 
80ba113a94SRemi Denis-Courmont 	sock_init_data(sock, sk);
81ba113a94SRemi Denis-Courmont 	sock->state = SS_UNCONNECTED;
82ba113a94SRemi Denis-Courmont 	sock->ops = pnp->ops;
83ba113a94SRemi Denis-Courmont 	sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
84ba113a94SRemi Denis-Courmont 	sk->sk_protocol = protocol;
85ba113a94SRemi Denis-Courmont 	pn = pn_sk(sk);
86ba113a94SRemi Denis-Courmont 	pn->sobject = 0;
87ba113a94SRemi Denis-Courmont 	pn->resource = 0;
88ba113a94SRemi Denis-Courmont 	sk->sk_prot->init(sk);
89ba113a94SRemi Denis-Courmont 	err = 0;
904b07b3f6SRemi Denis-Courmont 
914b07b3f6SRemi Denis-Courmont out:
924b07b3f6SRemi Denis-Courmont 	phonet_proto_put(pnp);
934b07b3f6SRemi Denis-Courmont 	return err;
944b07b3f6SRemi Denis-Courmont }
954b07b3f6SRemi Denis-Courmont 
964b07b3f6SRemi Denis-Courmont static struct net_proto_family phonet_proto_family = {
974b07b3f6SRemi Denis-Courmont 	.family = AF_PHONET,
984b07b3f6SRemi Denis-Courmont 	.create = pn_socket_create,
994b07b3f6SRemi Denis-Courmont 	.owner = THIS_MODULE,
1004b07b3f6SRemi Denis-Courmont };
1014b07b3f6SRemi Denis-Courmont 
102107d0d9bSRemi Denis-Courmont /*
103107d0d9bSRemi Denis-Courmont  * Prepends an ISI header and sends a datagram.
104107d0d9bSRemi Denis-Courmont  */
105107d0d9bSRemi Denis-Courmont static int pn_send(struct sk_buff *skb, struct net_device *dev,
106107d0d9bSRemi Denis-Courmont 			u16 dst, u16 src, u8 res)
107107d0d9bSRemi Denis-Courmont {
108107d0d9bSRemi Denis-Courmont 	struct phonethdr *ph;
109107d0d9bSRemi Denis-Courmont 	int err;
110107d0d9bSRemi Denis-Courmont 
111107d0d9bSRemi Denis-Courmont 	if (skb->len + 2 > 0xffff) {
112107d0d9bSRemi Denis-Courmont 		/* Phonet length field would overflow */
113107d0d9bSRemi Denis-Courmont 		err = -EMSGSIZE;
114107d0d9bSRemi Denis-Courmont 		goto drop;
115107d0d9bSRemi Denis-Courmont 	}
116107d0d9bSRemi Denis-Courmont 
117107d0d9bSRemi Denis-Courmont 	skb_reset_transport_header(skb);
118107d0d9bSRemi Denis-Courmont 	WARN_ON(skb_headroom(skb) & 1); /* HW assumes word alignment */
119107d0d9bSRemi Denis-Courmont 	skb_push(skb, sizeof(struct phonethdr));
120107d0d9bSRemi Denis-Courmont 	skb_reset_network_header(skb);
121107d0d9bSRemi Denis-Courmont 	ph = pn_hdr(skb);
122107d0d9bSRemi Denis-Courmont 	ph->pn_rdev = pn_dev(dst);
123107d0d9bSRemi Denis-Courmont 	ph->pn_sdev = pn_dev(src);
124107d0d9bSRemi Denis-Courmont 	ph->pn_res = res;
125107d0d9bSRemi Denis-Courmont 	ph->pn_length = __cpu_to_be16(skb->len + 2 - sizeof(*ph));
126107d0d9bSRemi Denis-Courmont 	ph->pn_robj = pn_obj(dst);
127107d0d9bSRemi Denis-Courmont 	ph->pn_sobj = pn_obj(src);
128107d0d9bSRemi Denis-Courmont 
129107d0d9bSRemi Denis-Courmont 	skb->protocol = htons(ETH_P_PHONET);
130107d0d9bSRemi Denis-Courmont 	skb->priority = 0;
131107d0d9bSRemi Denis-Courmont 	skb->dev = dev;
132107d0d9bSRemi Denis-Courmont 
133107d0d9bSRemi Denis-Courmont 	if (pn_addr(src) == pn_addr(dst)) {
134107d0d9bSRemi Denis-Courmont 		skb_reset_mac_header(skb);
135107d0d9bSRemi Denis-Courmont 		skb->pkt_type = PACKET_LOOPBACK;
136107d0d9bSRemi Denis-Courmont 		skb_orphan(skb);
137107d0d9bSRemi Denis-Courmont 		netif_rx_ni(skb);
138107d0d9bSRemi Denis-Courmont 		err = 0;
139107d0d9bSRemi Denis-Courmont 	} else {
140107d0d9bSRemi Denis-Courmont 		err = dev_hard_header(skb, dev, ntohs(skb->protocol),
141107d0d9bSRemi Denis-Courmont 					NULL, NULL, skb->len);
142107d0d9bSRemi Denis-Courmont 		if (err < 0) {
143107d0d9bSRemi Denis-Courmont 			err = -EHOSTUNREACH;
144107d0d9bSRemi Denis-Courmont 			goto drop;
145107d0d9bSRemi Denis-Courmont 		}
146107d0d9bSRemi Denis-Courmont 		err = dev_queue_xmit(skb);
147107d0d9bSRemi Denis-Courmont 	}
148107d0d9bSRemi Denis-Courmont 
149107d0d9bSRemi Denis-Courmont 	return err;
150107d0d9bSRemi Denis-Courmont drop:
151107d0d9bSRemi Denis-Courmont 	kfree_skb(skb);
152107d0d9bSRemi Denis-Courmont 	return err;
153107d0d9bSRemi Denis-Courmont }
154107d0d9bSRemi Denis-Courmont 
155107d0d9bSRemi Denis-Courmont /*
156107d0d9bSRemi Denis-Courmont  * Create a Phonet header for the skb and send it out. Returns
157107d0d9bSRemi Denis-Courmont  * non-zero error code if failed. The skb is freed then.
158107d0d9bSRemi Denis-Courmont  */
159107d0d9bSRemi Denis-Courmont int pn_skb_send(struct sock *sk, struct sk_buff *skb,
160107d0d9bSRemi Denis-Courmont 		const struct sockaddr_pn *target)
161107d0d9bSRemi Denis-Courmont {
162107d0d9bSRemi Denis-Courmont 	struct net_device *dev;
163107d0d9bSRemi Denis-Courmont 	struct pn_sock *pn = pn_sk(sk);
164107d0d9bSRemi Denis-Courmont 	int err;
165107d0d9bSRemi Denis-Courmont 	u16 src;
166107d0d9bSRemi Denis-Courmont 	u8 daddr = pn_sockaddr_get_addr(target), saddr = PN_NO_ADDR;
167107d0d9bSRemi Denis-Courmont 
168107d0d9bSRemi Denis-Courmont 	err = -EHOSTUNREACH;
169107d0d9bSRemi Denis-Courmont 	if (sk->sk_bound_dev_if)
170107d0d9bSRemi Denis-Courmont 		dev = dev_get_by_index(sock_net(sk), sk->sk_bound_dev_if);
171107d0d9bSRemi Denis-Courmont 	else
172107d0d9bSRemi Denis-Courmont 		dev = phonet_device_get(sock_net(sk));
173107d0d9bSRemi Denis-Courmont 	if (!dev || !(dev->flags & IFF_UP))
174107d0d9bSRemi Denis-Courmont 		goto drop;
175107d0d9bSRemi Denis-Courmont 
176107d0d9bSRemi Denis-Courmont 	saddr = phonet_address_get(dev, daddr);
177107d0d9bSRemi Denis-Courmont 	if (saddr == PN_NO_ADDR)
178107d0d9bSRemi Denis-Courmont 		goto drop;
179107d0d9bSRemi Denis-Courmont 
180107d0d9bSRemi Denis-Courmont 	src = pn->sobject;
181107d0d9bSRemi Denis-Courmont 	if (!pn_addr(src))
182107d0d9bSRemi Denis-Courmont 		src = pn_object(saddr, pn_obj(src));
183107d0d9bSRemi Denis-Courmont 
184107d0d9bSRemi Denis-Courmont 	err = pn_send(skb, dev, pn_sockaddr_get_object(target),
185107d0d9bSRemi Denis-Courmont 			src, pn_sockaddr_get_resource(target));
186107d0d9bSRemi Denis-Courmont 	dev_put(dev);
187107d0d9bSRemi Denis-Courmont 	return err;
188107d0d9bSRemi Denis-Courmont 
189107d0d9bSRemi Denis-Courmont drop:
190107d0d9bSRemi Denis-Courmont 	kfree_skb(skb);
191107d0d9bSRemi Denis-Courmont 	if (dev)
192107d0d9bSRemi Denis-Courmont 		dev_put(dev);
193107d0d9bSRemi Denis-Courmont 	return err;
194107d0d9bSRemi Denis-Courmont }
195107d0d9bSRemi Denis-Courmont EXPORT_SYMBOL(pn_skb_send);
196107d0d9bSRemi Denis-Courmont 
1974b07b3f6SRemi Denis-Courmont /* packet type functions */
1984b07b3f6SRemi Denis-Courmont 
1994b07b3f6SRemi Denis-Courmont /*
2004b07b3f6SRemi Denis-Courmont  * Stuff received packets to associated sockets.
2014b07b3f6SRemi Denis-Courmont  * On error, returns non-zero and releases the skb.
2024b07b3f6SRemi Denis-Courmont  */
2034b07b3f6SRemi Denis-Courmont static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
2044b07b3f6SRemi Denis-Courmont 			struct packet_type *pkttype,
2054b07b3f6SRemi Denis-Courmont 			struct net_device *orig_dev)
2064b07b3f6SRemi Denis-Courmont {
2074b07b3f6SRemi Denis-Courmont 	struct phonethdr *ph;
208ba113a94SRemi Denis-Courmont 	struct sock *sk;
2094b07b3f6SRemi Denis-Courmont 	struct sockaddr_pn sa;
2104b07b3f6SRemi Denis-Courmont 	u16 len;
2114b07b3f6SRemi Denis-Courmont 
2124b07b3f6SRemi Denis-Courmont 	if (dev_net(dev) != &init_net)
2134b07b3f6SRemi Denis-Courmont 		goto out;
2144b07b3f6SRemi Denis-Courmont 
2154b07b3f6SRemi Denis-Courmont 	/* check we have at least a full Phonet header */
2164b07b3f6SRemi Denis-Courmont 	if (!pskb_pull(skb, sizeof(struct phonethdr)))
2174b07b3f6SRemi Denis-Courmont 		goto out;
2184b07b3f6SRemi Denis-Courmont 
2194b07b3f6SRemi Denis-Courmont 	/* check that the advertised length is correct */
2204b07b3f6SRemi Denis-Courmont 	ph = pn_hdr(skb);
2214b07b3f6SRemi Denis-Courmont 	len = get_unaligned_be16(&ph->pn_length);
2224b07b3f6SRemi Denis-Courmont 	if (len < 2)
2234b07b3f6SRemi Denis-Courmont 		goto out;
2244b07b3f6SRemi Denis-Courmont 	len -= 2;
2254b07b3f6SRemi Denis-Courmont 	if ((len > skb->len) || pskb_trim(skb, len))
2264b07b3f6SRemi Denis-Courmont 		goto out;
2274b07b3f6SRemi Denis-Courmont 	skb_reset_transport_header(skb);
2284b07b3f6SRemi Denis-Courmont 
2294b07b3f6SRemi Denis-Courmont 	pn_skb_get_dst_sockaddr(skb, &sa);
2304b07b3f6SRemi Denis-Courmont 	if (pn_sockaddr_get_addr(&sa) == 0)
2314b07b3f6SRemi Denis-Courmont 		goto out; /* currently, we cannot be device 0 */
2324b07b3f6SRemi Denis-Courmont 
233ba113a94SRemi Denis-Courmont 	sk = pn_find_sock_by_sa(&sa);
234ba113a94SRemi Denis-Courmont 	if (sk == NULL)
235ba113a94SRemi Denis-Courmont 		goto out;
236ba113a94SRemi Denis-Courmont 
237ba113a94SRemi Denis-Courmont 	/* Push data to the socket (or other sockets connected to it). */
238ba113a94SRemi Denis-Courmont 	return sk_receive_skb(sk, skb, 0);
2394b07b3f6SRemi Denis-Courmont 
2404b07b3f6SRemi Denis-Courmont out:
2414b07b3f6SRemi Denis-Courmont 	kfree_skb(skb);
2424b07b3f6SRemi Denis-Courmont 	return NET_RX_DROP;
2434b07b3f6SRemi Denis-Courmont }
2444b07b3f6SRemi Denis-Courmont 
2454b07b3f6SRemi Denis-Courmont static struct packet_type phonet_packet_type = {
2464b07b3f6SRemi Denis-Courmont 	.type = __constant_htons(ETH_P_PHONET),
2474b07b3f6SRemi Denis-Courmont 	.dev = NULL,
2484b07b3f6SRemi Denis-Courmont 	.func = phonet_rcv,
2494b07b3f6SRemi Denis-Courmont };
2504b07b3f6SRemi Denis-Courmont 
2514b07b3f6SRemi Denis-Courmont /* Transport protocol registration */
2524b07b3f6SRemi Denis-Courmont static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly;
2534b07b3f6SRemi Denis-Courmont static DEFINE_SPINLOCK(proto_tab_lock);
2544b07b3f6SRemi Denis-Courmont 
2554b07b3f6SRemi Denis-Courmont int __init_or_module phonet_proto_register(int protocol,
2564b07b3f6SRemi Denis-Courmont 						struct phonet_protocol *pp)
2574b07b3f6SRemi Denis-Courmont {
2584b07b3f6SRemi Denis-Courmont 	int err = 0;
2594b07b3f6SRemi Denis-Courmont 
2604b07b3f6SRemi Denis-Courmont 	if (protocol >= PHONET_NPROTO)
2614b07b3f6SRemi Denis-Courmont 		return -EINVAL;
2624b07b3f6SRemi Denis-Courmont 
2634b07b3f6SRemi Denis-Courmont 	err = proto_register(pp->prot, 1);
2644b07b3f6SRemi Denis-Courmont 	if (err)
2654b07b3f6SRemi Denis-Courmont 		return err;
2664b07b3f6SRemi Denis-Courmont 
2674b07b3f6SRemi Denis-Courmont 	spin_lock(&proto_tab_lock);
2684b07b3f6SRemi Denis-Courmont 	if (proto_tab[protocol])
2694b07b3f6SRemi Denis-Courmont 		err = -EBUSY;
2704b07b3f6SRemi Denis-Courmont 	else
2714b07b3f6SRemi Denis-Courmont 		proto_tab[protocol] = pp;
2724b07b3f6SRemi Denis-Courmont 	spin_unlock(&proto_tab_lock);
2734b07b3f6SRemi Denis-Courmont 
2744b07b3f6SRemi Denis-Courmont 	return err;
2754b07b3f6SRemi Denis-Courmont }
2764b07b3f6SRemi Denis-Courmont EXPORT_SYMBOL(phonet_proto_register);
2774b07b3f6SRemi Denis-Courmont 
2784b07b3f6SRemi Denis-Courmont void phonet_proto_unregister(int protocol, struct phonet_protocol *pp)
2794b07b3f6SRemi Denis-Courmont {
2804b07b3f6SRemi Denis-Courmont 	spin_lock(&proto_tab_lock);
2814b07b3f6SRemi Denis-Courmont 	BUG_ON(proto_tab[protocol] != pp);
2824b07b3f6SRemi Denis-Courmont 	proto_tab[protocol] = NULL;
2834b07b3f6SRemi Denis-Courmont 	spin_unlock(&proto_tab_lock);
2844b07b3f6SRemi Denis-Courmont 	proto_unregister(pp->prot);
2854b07b3f6SRemi Denis-Courmont }
2864b07b3f6SRemi Denis-Courmont EXPORT_SYMBOL(phonet_proto_unregister);
2874b07b3f6SRemi Denis-Courmont 
2884b07b3f6SRemi Denis-Courmont static struct phonet_protocol *phonet_proto_get(int protocol)
2894b07b3f6SRemi Denis-Courmont {
2904b07b3f6SRemi Denis-Courmont 	struct phonet_protocol *pp;
2914b07b3f6SRemi Denis-Courmont 
2924b07b3f6SRemi Denis-Courmont 	if (protocol >= PHONET_NPROTO)
2934b07b3f6SRemi Denis-Courmont 		return NULL;
2944b07b3f6SRemi Denis-Courmont 
2954b07b3f6SRemi Denis-Courmont 	spin_lock(&proto_tab_lock);
2964b07b3f6SRemi Denis-Courmont 	pp = proto_tab[protocol];
2974b07b3f6SRemi Denis-Courmont 	if (pp && !try_module_get(pp->prot->owner))
2984b07b3f6SRemi Denis-Courmont 		pp = NULL;
2994b07b3f6SRemi Denis-Courmont 	spin_unlock(&proto_tab_lock);
3004b07b3f6SRemi Denis-Courmont 
3014b07b3f6SRemi Denis-Courmont 	return pp;
3024b07b3f6SRemi Denis-Courmont }
3034b07b3f6SRemi Denis-Courmont 
3044b07b3f6SRemi Denis-Courmont static inline void phonet_proto_put(struct phonet_protocol *pp)
3054b07b3f6SRemi Denis-Courmont {
3064b07b3f6SRemi Denis-Courmont 	module_put(pp->prot->owner);
3074b07b3f6SRemi Denis-Courmont }
3084b07b3f6SRemi Denis-Courmont 
3094b07b3f6SRemi Denis-Courmont /* Module registration */
3104b07b3f6SRemi Denis-Courmont static int __init phonet_init(void)
3114b07b3f6SRemi Denis-Courmont {
3124b07b3f6SRemi Denis-Courmont 	int err;
3134b07b3f6SRemi Denis-Courmont 
3144b07b3f6SRemi Denis-Courmont 	err = sock_register(&phonet_proto_family);
3154b07b3f6SRemi Denis-Courmont 	if (err) {
3164b07b3f6SRemi Denis-Courmont 		printk(KERN_ALERT
3174b07b3f6SRemi Denis-Courmont 			"phonet protocol family initialization failed\n");
3184b07b3f6SRemi Denis-Courmont 		return err;
3194b07b3f6SRemi Denis-Courmont 	}
3204b07b3f6SRemi Denis-Courmont 
321f8ff6028SRemi Denis-Courmont 	phonet_device_init();
3224b07b3f6SRemi Denis-Courmont 	dev_add_pack(&phonet_packet_type);
3238fb39740SRemi Denis-Courmont 	phonet_netlink_register();
324107d0d9bSRemi Denis-Courmont 
325107d0d9bSRemi Denis-Courmont 	err = isi_register();
326107d0d9bSRemi Denis-Courmont 	if (err)
327107d0d9bSRemi Denis-Courmont 		goto err;
3284b07b3f6SRemi Denis-Courmont 	return 0;
329107d0d9bSRemi Denis-Courmont 
330107d0d9bSRemi Denis-Courmont err:
331107d0d9bSRemi Denis-Courmont 	sock_unregister(AF_PHONET);
332107d0d9bSRemi Denis-Courmont 	dev_remove_pack(&phonet_packet_type);
333107d0d9bSRemi Denis-Courmont 	phonet_device_exit();
334107d0d9bSRemi Denis-Courmont 	return err;
3354b07b3f6SRemi Denis-Courmont }
3364b07b3f6SRemi Denis-Courmont 
3374b07b3f6SRemi Denis-Courmont static void __exit phonet_exit(void)
3384b07b3f6SRemi Denis-Courmont {
339107d0d9bSRemi Denis-Courmont 	isi_unregister();
3404b07b3f6SRemi Denis-Courmont 	sock_unregister(AF_PHONET);
3414b07b3f6SRemi Denis-Courmont 	dev_remove_pack(&phonet_packet_type);
342f8ff6028SRemi Denis-Courmont 	phonet_device_exit();
3434b07b3f6SRemi Denis-Courmont }
3444b07b3f6SRemi Denis-Courmont 
3454b07b3f6SRemi Denis-Courmont module_init(phonet_init);
3464b07b3f6SRemi Denis-Courmont module_exit(phonet_exit);
3474b07b3f6SRemi Denis-Courmont MODULE_DESCRIPTION("Phonet protocol stack for Linux");
3484b07b3f6SRemi Denis-Courmont MODULE_LICENSE("GPL");
349