1 /* Copyright 2011, Siemens AG 2 * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> 3 */ 4 5 /* Based on patches from Jon Smirl <jonsmirl@gmail.com> 6 * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 10 * as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 */ 17 18 /* Jon's code is based on 6lowpan implementation for Contiki which is: 19 * Copyright (c) 2008, Swedish Institute of Computer Science. 20 * All rights reserved. 21 * 22 * Redistribution and use in source and binary forms, with or without 23 * modification, are permitted provided that the following conditions 24 * are met: 25 * 1. Redistributions of source code must retain the above copyright 26 * notice, this list of conditions and the following disclaimer. 27 * 2. Redistributions in binary form must reproduce the above copyright 28 * notice, this list of conditions and the following disclaimer in the 29 * documentation and/or other materials provided with the distribution. 30 * 3. Neither the name of the Institute nor the names of its contributors 31 * may be used to endorse or promote products derived from this software 32 * without specific prior written permission. 33 * 34 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 37 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 44 * SUCH DAMAGE. 45 */ 46 47 #include <linux/module.h> 48 #include <linux/netdevice.h> 49 #include <linux/ieee802154.h> 50 51 #include <net/ipv6.h> 52 53 #include "6lowpan_i.h" 54 55 LIST_HEAD(lowpan_devices); 56 static int lowpan_open_count; 57 58 static __le16 lowpan_get_pan_id(const struct net_device *dev) 59 { 60 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 61 62 return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); 63 } 64 65 static __le16 lowpan_get_short_addr(const struct net_device *dev) 66 { 67 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 68 69 return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); 70 } 71 72 static u8 lowpan_get_dsn(const struct net_device *dev) 73 { 74 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 75 76 return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); 77 } 78 79 static struct header_ops lowpan_header_ops = { 80 .create = lowpan_header_create, 81 }; 82 83 static struct lock_class_key lowpan_tx_busylock; 84 static struct lock_class_key lowpan_netdev_xmit_lock_key; 85 86 static void lowpan_set_lockdep_class_one(struct net_device *dev, 87 struct netdev_queue *txq, 88 void *_unused) 89 { 90 lockdep_set_class(&txq->_xmit_lock, 91 &lowpan_netdev_xmit_lock_key); 92 } 93 94 static int lowpan_dev_init(struct net_device *dev) 95 { 96 netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); 97 dev->qdisc_tx_busylock = &lowpan_tx_busylock; 98 return 0; 99 } 100 101 static const struct net_device_ops lowpan_netdev_ops = { 102 .ndo_init = lowpan_dev_init, 103 .ndo_start_xmit = lowpan_xmit, 104 }; 105 106 static struct ieee802154_mlme_ops lowpan_mlme = { 107 .get_pan_id = lowpan_get_pan_id, 108 .get_short_addr = lowpan_get_short_addr, 109 .get_dsn = lowpan_get_dsn, 110 }; 111 112 static void lowpan_setup(struct net_device *dev) 113 { 114 dev->addr_len = IEEE802154_ADDR_LEN; 115 memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); 116 dev->type = ARPHRD_IEEE802154; 117 /* Frame Control + Sequence Number + Address fields + Security Header */ 118 dev->hard_header_len = 2 + 1 + 20 + 14; 119 dev->needed_tailroom = 2; /* FCS */ 120 dev->mtu = IPV6_MIN_MTU; 121 dev->tx_queue_len = 0; 122 dev->flags = IFF_BROADCAST | IFF_MULTICAST; 123 dev->watchdog_timeo = 0; 124 125 dev->netdev_ops = &lowpan_netdev_ops; 126 dev->header_ops = &lowpan_header_ops; 127 dev->ml_priv = &lowpan_mlme; 128 dev->destructor = free_netdev; 129 } 130 131 static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) 132 { 133 if (tb[IFLA_ADDRESS]) { 134 if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) 135 return -EINVAL; 136 } 137 return 0; 138 } 139 140 static int lowpan_newlink(struct net *src_net, struct net_device *dev, 141 struct nlattr *tb[], struct nlattr *data[]) 142 { 143 struct net_device *real_dev; 144 struct lowpan_dev_record *entry; 145 int ret; 146 147 ASSERT_RTNL(); 148 149 pr_debug("adding new link\n"); 150 151 if (!tb[IFLA_LINK]) 152 return -EINVAL; 153 /* find and hold real wpan device */ 154 real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); 155 if (!real_dev) 156 return -ENODEV; 157 if (real_dev->type != ARPHRD_IEEE802154) { 158 dev_put(real_dev); 159 return -EINVAL; 160 } 161 162 lowpan_dev_info(dev)->real_dev = real_dev; 163 mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); 164 165 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 166 if (!entry) { 167 dev_put(real_dev); 168 lowpan_dev_info(dev)->real_dev = NULL; 169 return -ENOMEM; 170 } 171 172 entry->ldev = dev; 173 174 /* Set the lowpan hardware address to the wpan hardware address. */ 175 memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); 176 177 mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); 178 INIT_LIST_HEAD(&entry->list); 179 list_add_tail(&entry->list, &lowpan_devices); 180 mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); 181 182 ret = register_netdevice(dev); 183 if (ret >= 0) { 184 if (!lowpan_open_count) 185 lowpan_rx_init(); 186 lowpan_open_count++; 187 } 188 189 return ret; 190 } 191 192 static void lowpan_dellink(struct net_device *dev, struct list_head *head) 193 { 194 struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); 195 struct net_device *real_dev = lowpan_dev->real_dev; 196 struct lowpan_dev_record *entry, *tmp; 197 198 ASSERT_RTNL(); 199 200 lowpan_open_count--; 201 if (!lowpan_open_count) 202 lowpan_rx_exit(); 203 204 mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); 205 list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { 206 if (entry->ldev == dev) { 207 list_del(&entry->list); 208 kfree(entry); 209 } 210 } 211 mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); 212 213 mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); 214 215 unregister_netdevice_queue(dev, head); 216 217 dev_put(real_dev); 218 } 219 220 static struct rtnl_link_ops lowpan_link_ops __read_mostly = { 221 .kind = "lowpan", 222 .priv_size = sizeof(struct lowpan_dev_info), 223 .setup = lowpan_setup, 224 .newlink = lowpan_newlink, 225 .dellink = lowpan_dellink, 226 .validate = lowpan_validate, 227 }; 228 229 static inline int __init lowpan_netlink_init(void) 230 { 231 return rtnl_link_register(&lowpan_link_ops); 232 } 233 234 static inline void lowpan_netlink_fini(void) 235 { 236 rtnl_link_unregister(&lowpan_link_ops); 237 } 238 239 static int lowpan_device_event(struct notifier_block *unused, 240 unsigned long event, void *ptr) 241 { 242 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 243 LIST_HEAD(del_list); 244 struct lowpan_dev_record *entry, *tmp; 245 246 if (dev->type != ARPHRD_IEEE802154) 247 goto out; 248 249 if (event == NETDEV_UNREGISTER) { 250 list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { 251 if (lowpan_dev_info(entry->ldev)->real_dev == dev) 252 lowpan_dellink(entry->ldev, &del_list); 253 } 254 255 unregister_netdevice_many(&del_list); 256 } 257 258 out: 259 return NOTIFY_DONE; 260 } 261 262 static struct notifier_block lowpan_dev_notifier = { 263 .notifier_call = lowpan_device_event, 264 }; 265 266 static int __init lowpan_init_module(void) 267 { 268 int err = 0; 269 270 err = lowpan_net_frag_init(); 271 if (err < 0) 272 goto out; 273 274 err = lowpan_netlink_init(); 275 if (err < 0) 276 goto out_frag; 277 278 err = register_netdevice_notifier(&lowpan_dev_notifier); 279 if (err < 0) 280 goto out_pack; 281 282 return 0; 283 284 out_pack: 285 lowpan_netlink_fini(); 286 out_frag: 287 lowpan_net_frag_exit(); 288 out: 289 return err; 290 } 291 292 static void __exit lowpan_cleanup_module(void) 293 { 294 lowpan_netlink_fini(); 295 296 lowpan_net_frag_exit(); 297 298 unregister_netdevice_notifier(&lowpan_dev_notifier); 299 } 300 301 module_init(lowpan_init_module); 302 module_exit(lowpan_cleanup_module); 303 MODULE_LICENSE("GPL"); 304 MODULE_ALIAS_RTNL_LINK("lowpan"); 305