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