Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.26-rc2 382 lines 8.1 kB view raw
1/* 2 * Generic HDLC support routines for Linux 3 * 4 * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of version 2 of the GNU General Public License 8 * as published by the Free Software Foundation. 9 * 10 * Currently supported: 11 * * raw IP-in-HDLC 12 * * Cisco HDLC 13 * * Frame Relay with ANSI or CCITT LMI (both user and network side) 14 * * PPP 15 * * X.25 16 * 17 * Use sethdlc utility to set line parameters, protocol and PVCs 18 * 19 * How does it work: 20 * - proto->open(), close(), start(), stop() calls are serialized. 21 * The order is: open, [ start, stop ... ] close ... 22 * - proto->start() and stop() are called with spin_lock_irq held. 23 */ 24 25#include <linux/module.h> 26#include <linux/kernel.h> 27#include <linux/slab.h> 28#include <linux/poll.h> 29#include <linux/errno.h> 30#include <linux/if_arp.h> 31#include <linux/init.h> 32#include <linux/skbuff.h> 33#include <linux/pkt_sched.h> 34#include <linux/inetdevice.h> 35#include <linux/lapb.h> 36#include <linux/rtnetlink.h> 37#include <linux/notifier.h> 38#include <linux/hdlc.h> 39#include <net/net_namespace.h> 40 41 42static const char* version = "HDLC support module revision 1.22"; 43 44#undef DEBUG_LINK 45 46static struct hdlc_proto *first_proto = NULL; 47 48 49static int hdlc_change_mtu(struct net_device *dev, int new_mtu) 50{ 51 if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU)) 52 return -EINVAL; 53 dev->mtu = new_mtu; 54 return 0; 55} 56 57 58 59static struct net_device_stats *hdlc_get_stats(struct net_device *dev) 60{ 61 return hdlc_stats(dev); 62} 63 64 65 66static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, 67 struct packet_type *p, struct net_device *orig_dev) 68{ 69 struct hdlc_device *hdlc = dev_to_hdlc(dev); 70 71 if (dev_net(dev) != &init_net) { 72 kfree_skb(skb); 73 return 0; 74 } 75 76 BUG_ON(!hdlc->proto->netif_rx); 77 return hdlc->proto->netif_rx(skb); 78} 79 80 81 82static inline void hdlc_proto_start(struct net_device *dev) 83{ 84 hdlc_device *hdlc = dev_to_hdlc(dev); 85 if (hdlc->proto->start) 86 hdlc->proto->start(dev); 87} 88 89 90 91static inline void hdlc_proto_stop(struct net_device *dev) 92{ 93 hdlc_device *hdlc = dev_to_hdlc(dev); 94 if (hdlc->proto->stop) 95 hdlc->proto->stop(dev); 96} 97 98 99 100static int hdlc_device_event(struct notifier_block *this, unsigned long event, 101 void *ptr) 102{ 103 struct net_device *dev = ptr; 104 hdlc_device *hdlc; 105 unsigned long flags; 106 int on; 107 108 if (dev_net(dev) != &init_net) 109 return NOTIFY_DONE; 110 111 if (dev->get_stats != hdlc_get_stats) 112 return NOTIFY_DONE; /* not an HDLC device */ 113 114 if (event != NETDEV_CHANGE) 115 return NOTIFY_DONE; /* Only interrested in carrier changes */ 116 117 on = netif_carrier_ok(dev); 118 119#ifdef DEBUG_LINK 120 printk(KERN_DEBUG "%s: hdlc_device_event NETDEV_CHANGE, carrier %i\n", 121 dev->name, on); 122#endif 123 124 hdlc = dev_to_hdlc(dev); 125 spin_lock_irqsave(&hdlc->state_lock, flags); 126 127 if (hdlc->carrier == on) 128 goto carrier_exit; /* no change in DCD line level */ 129 130 hdlc->carrier = on; 131 132 if (!hdlc->open) 133 goto carrier_exit; 134 135 if (hdlc->carrier) { 136 printk(KERN_INFO "%s: Carrier detected\n", dev->name); 137 hdlc_proto_start(dev); 138 } else { 139 printk(KERN_INFO "%s: Carrier lost\n", dev->name); 140 hdlc_proto_stop(dev); 141 } 142 143carrier_exit: 144 spin_unlock_irqrestore(&hdlc->state_lock, flags); 145 return NOTIFY_DONE; 146} 147 148 149 150/* Must be called by hardware driver when HDLC device is being opened */ 151int hdlc_open(struct net_device *dev) 152{ 153 hdlc_device *hdlc = dev_to_hdlc(dev); 154#ifdef DEBUG_LINK 155 printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name, 156 hdlc->carrier, hdlc->open); 157#endif 158 159 if (hdlc->proto == NULL) 160 return -ENOSYS; /* no protocol attached */ 161 162 if (hdlc->proto->open) { 163 int result = hdlc->proto->open(dev); 164 if (result) 165 return result; 166 } 167 168 spin_lock_irq(&hdlc->state_lock); 169 170 if (hdlc->carrier) { 171 printk(KERN_INFO "%s: Carrier detected\n", dev->name); 172 hdlc_proto_start(dev); 173 } else 174 printk(KERN_INFO "%s: No carrier\n", dev->name); 175 176 hdlc->open = 1; 177 178 spin_unlock_irq(&hdlc->state_lock); 179 return 0; 180} 181 182 183 184/* Must be called by hardware driver when HDLC device is being closed */ 185void hdlc_close(struct net_device *dev) 186{ 187 hdlc_device *hdlc = dev_to_hdlc(dev); 188#ifdef DEBUG_LINK 189 printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name, 190 hdlc->carrier, hdlc->open); 191#endif 192 193 spin_lock_irq(&hdlc->state_lock); 194 195 hdlc->open = 0; 196 if (hdlc->carrier) 197 hdlc_proto_stop(dev); 198 199 spin_unlock_irq(&hdlc->state_lock); 200 201 if (hdlc->proto->close) 202 hdlc->proto->close(dev); 203} 204 205 206 207int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 208{ 209 struct hdlc_proto *proto = first_proto; 210 int result; 211 212 if (cmd != SIOCWANDEV) 213 return -EINVAL; 214 215 if (dev_to_hdlc(dev)->proto) { 216 result = dev_to_hdlc(dev)->proto->ioctl(dev, ifr); 217 if (result != -EINVAL) 218 return result; 219 } 220 221 /* Not handled by currently attached protocol (if any) */ 222 223 while (proto) { 224 if ((result = proto->ioctl(dev, ifr)) != -EINVAL) 225 return result; 226 proto = proto->next; 227 } 228 return -EINVAL; 229} 230 231static const struct header_ops hdlc_null_ops; 232 233static void hdlc_setup_dev(struct net_device *dev) 234{ 235 /* Re-init all variables changed by HDLC protocol drivers, 236 * including ether_setup() called from hdlc_raw_eth.c. 237 */ 238 dev->get_stats = hdlc_get_stats; 239 dev->flags = IFF_POINTOPOINT | IFF_NOARP; 240 dev->mtu = HDLC_MAX_MTU; 241 dev->type = ARPHRD_RAWHDLC; 242 dev->hard_header_len = 16; 243 dev->addr_len = 0; 244 dev->header_ops = &hdlc_null_ops; 245 246 dev->change_mtu = hdlc_change_mtu; 247} 248 249static void hdlc_setup(struct net_device *dev) 250{ 251 hdlc_device *hdlc = dev_to_hdlc(dev); 252 253 hdlc_setup_dev(dev); 254 hdlc->carrier = 1; 255 hdlc->open = 0; 256 spin_lock_init(&hdlc->state_lock); 257} 258 259struct net_device *alloc_hdlcdev(void *priv) 260{ 261 struct net_device *dev; 262 dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d", hdlc_setup); 263 if (dev) 264 dev_to_hdlc(dev)->priv = priv; 265 return dev; 266} 267 268void unregister_hdlc_device(struct net_device *dev) 269{ 270 rtnl_lock(); 271 unregister_netdevice(dev); 272 detach_hdlc_protocol(dev); 273 rtnl_unlock(); 274} 275 276 277 278int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, 279 size_t size) 280{ 281 detach_hdlc_protocol(dev); 282 283 if (!try_module_get(proto->module)) 284 return -ENOSYS; 285 286 if (size) 287 if ((dev_to_hdlc(dev)->state = kmalloc(size, 288 GFP_KERNEL)) == NULL) { 289 printk(KERN_WARNING "Memory squeeze on" 290 " hdlc_proto_attach()\n"); 291 module_put(proto->module); 292 return -ENOBUFS; 293 } 294 dev_to_hdlc(dev)->proto = proto; 295 return 0; 296} 297 298 299void detach_hdlc_protocol(struct net_device *dev) 300{ 301 hdlc_device *hdlc = dev_to_hdlc(dev); 302 303 if (hdlc->proto) { 304 if (hdlc->proto->detach) 305 hdlc->proto->detach(dev); 306 module_put(hdlc->proto->module); 307 hdlc->proto = NULL; 308 } 309 kfree(hdlc->state); 310 hdlc->state = NULL; 311 hdlc_setup_dev(dev); 312} 313 314 315void register_hdlc_protocol(struct hdlc_proto *proto) 316{ 317 proto->next = first_proto; 318 first_proto = proto; 319} 320 321 322void unregister_hdlc_protocol(struct hdlc_proto *proto) 323{ 324 struct hdlc_proto **p = &first_proto; 325 while (*p) { 326 if (*p == proto) { 327 *p = proto->next; 328 return; 329 } 330 p = &((*p)->next); 331 } 332} 333 334 335 336MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); 337MODULE_DESCRIPTION("HDLC support module"); 338MODULE_LICENSE("GPL v2"); 339 340EXPORT_SYMBOL(hdlc_open); 341EXPORT_SYMBOL(hdlc_close); 342EXPORT_SYMBOL(hdlc_ioctl); 343EXPORT_SYMBOL(alloc_hdlcdev); 344EXPORT_SYMBOL(unregister_hdlc_device); 345EXPORT_SYMBOL(register_hdlc_protocol); 346EXPORT_SYMBOL(unregister_hdlc_protocol); 347EXPORT_SYMBOL(attach_hdlc_protocol); 348EXPORT_SYMBOL(detach_hdlc_protocol); 349 350static struct packet_type hdlc_packet_type = { 351 .type = __constant_htons(ETH_P_HDLC), 352 .func = hdlc_rcv, 353}; 354 355 356static struct notifier_block hdlc_notifier = { 357 .notifier_call = hdlc_device_event, 358}; 359 360 361static int __init hdlc_module_init(void) 362{ 363 int result; 364 365 printk(KERN_INFO "%s\n", version); 366 if ((result = register_netdevice_notifier(&hdlc_notifier)) != 0) 367 return result; 368 dev_add_pack(&hdlc_packet_type); 369 return 0; 370} 371 372 373 374static void __exit hdlc_module_exit(void) 375{ 376 dev_remove_pack(&hdlc_packet_type); 377 unregister_netdevice_notifier(&hdlc_notifier); 378} 379 380 381module_init(hdlc_module_init); 382module_exit(hdlc_module_exit);