162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Generic HDLC support routines for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Currently supported: 862306a36Sopenharmony_ci * * raw IP-in-HDLC 962306a36Sopenharmony_ci * * Cisco HDLC 1062306a36Sopenharmony_ci * * Frame Relay with ANSI or CCITT LMI (both user and network side) 1162306a36Sopenharmony_ci * * PPP 1262306a36Sopenharmony_ci * * X.25 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Use sethdlc utility to set line parameters, protocol and PVCs 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * How does it work: 1762306a36Sopenharmony_ci * - proto->open(), close(), start(), stop() calls are serialized. 1862306a36Sopenharmony_ci * The order is: open, [ start, stop ... ] close ... 1962306a36Sopenharmony_ci * - proto->start() and stop() are called with spin_lock_irq held. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/errno.h> 2562306a36Sopenharmony_ci#include <linux/hdlc.h> 2662306a36Sopenharmony_ci#include <linux/if_arp.h> 2762306a36Sopenharmony_ci#include <linux/inetdevice.h> 2862306a36Sopenharmony_ci#include <linux/init.h> 2962306a36Sopenharmony_ci#include <linux/kernel.h> 3062306a36Sopenharmony_ci#include <linux/module.h> 3162306a36Sopenharmony_ci#include <linux/notifier.h> 3262306a36Sopenharmony_ci#include <linux/pkt_sched.h> 3362306a36Sopenharmony_ci#include <linux/poll.h> 3462306a36Sopenharmony_ci#include <linux/rtnetlink.h> 3562306a36Sopenharmony_ci#include <linux/skbuff.h> 3662306a36Sopenharmony_ci#include <linux/slab.h> 3762306a36Sopenharmony_ci#include <net/net_namespace.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic const char *version = "HDLC support module revision 1.22"; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#undef DEBUG_LINK 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct hdlc_proto *first_proto; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, 4662306a36Sopenharmony_ci struct packet_type *p, struct net_device *orig_dev) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct hdlc_device *hdlc; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* First make sure "dev" is an HDLC device */ 5162306a36Sopenharmony_ci if (!(dev->priv_flags & IFF_WAN_HDLC)) { 5262306a36Sopenharmony_ci kfree_skb(skb); 5362306a36Sopenharmony_ci return NET_RX_SUCCESS; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci hdlc = dev_to_hdlc(dev); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) { 5962306a36Sopenharmony_ci kfree_skb(skb); 6062306a36Sopenharmony_ci return 0; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci BUG_ON(!hdlc->proto->netif_rx); 6462306a36Sopenharmony_ci return hdlc->proto->netif_rx(skb); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cinetdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (hdlc->proto->xmit) 7262306a36Sopenharmony_ci return hdlc->proto->xmit(skb, dev); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return hdlc->xmit(skb, dev); /* call hardware driver directly */ 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ciEXPORT_SYMBOL(hdlc_start_xmit); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic inline void hdlc_proto_start(struct net_device *dev) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (hdlc->proto->start) 8362306a36Sopenharmony_ci hdlc->proto->start(dev); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic inline void hdlc_proto_stop(struct net_device *dev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (hdlc->proto->stop) 9162306a36Sopenharmony_ci hdlc->proto->stop(dev); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int hdlc_device_event(struct notifier_block *this, unsigned long event, 9562306a36Sopenharmony_ci void *ptr) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct net_device *dev = netdev_notifier_info_to_dev(ptr); 9862306a36Sopenharmony_ci hdlc_device *hdlc; 9962306a36Sopenharmony_ci unsigned long flags; 10062306a36Sopenharmony_ci int on; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci if (!net_eq(dev_net(dev), &init_net)) 10362306a36Sopenharmony_ci return NOTIFY_DONE; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!(dev->priv_flags & IFF_WAN_HDLC)) 10662306a36Sopenharmony_ci return NOTIFY_DONE; /* not an HDLC device */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (event != NETDEV_CHANGE) 10962306a36Sopenharmony_ci return NOTIFY_DONE; /* Only interested in carrier changes */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci on = netif_carrier_ok(dev); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#ifdef DEBUG_LINK 11462306a36Sopenharmony_ci printk(KERN_DEBUG "%s: hdlc_device_event NETDEV_CHANGE, carrier %i\n", 11562306a36Sopenharmony_ci dev->name, on); 11662306a36Sopenharmony_ci#endif 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci hdlc = dev_to_hdlc(dev); 11962306a36Sopenharmony_ci spin_lock_irqsave(&hdlc->state_lock, flags); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (hdlc->carrier == on) 12262306a36Sopenharmony_ci goto carrier_exit; /* no change in DCD line level */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci hdlc->carrier = on; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!hdlc->open) 12762306a36Sopenharmony_ci goto carrier_exit; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (hdlc->carrier) { 13062306a36Sopenharmony_ci netdev_info(dev, "Carrier detected\n"); 13162306a36Sopenharmony_ci hdlc_proto_start(dev); 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci netdev_info(dev, "Carrier lost\n"); 13462306a36Sopenharmony_ci hdlc_proto_stop(dev); 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cicarrier_exit: 13862306a36Sopenharmony_ci spin_unlock_irqrestore(&hdlc->state_lock, flags); 13962306a36Sopenharmony_ci return NOTIFY_DONE; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* Must be called by hardware driver when HDLC device is being opened */ 14362306a36Sopenharmony_ciint hdlc_open(struct net_device *dev) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 14662306a36Sopenharmony_ci#ifdef DEBUG_LINK 14762306a36Sopenharmony_ci printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name, 14862306a36Sopenharmony_ci hdlc->carrier, hdlc->open); 14962306a36Sopenharmony_ci#endif 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (!hdlc->proto) 15262306a36Sopenharmony_ci return -ENOSYS; /* no protocol attached */ 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (hdlc->proto->open) { 15562306a36Sopenharmony_ci int result = hdlc->proto->open(dev); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (result) 15862306a36Sopenharmony_ci return result; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci spin_lock_irq(&hdlc->state_lock); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (hdlc->carrier) { 16462306a36Sopenharmony_ci netdev_info(dev, "Carrier detected\n"); 16562306a36Sopenharmony_ci hdlc_proto_start(dev); 16662306a36Sopenharmony_ci } else { 16762306a36Sopenharmony_ci netdev_info(dev, "No carrier\n"); 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci hdlc->open = 1; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_unlock_irq(&hdlc->state_lock); 17362306a36Sopenharmony_ci return 0; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ciEXPORT_SYMBOL(hdlc_open); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/* Must be called by hardware driver when HDLC device is being closed */ 17862306a36Sopenharmony_civoid hdlc_close(struct net_device *dev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 18162306a36Sopenharmony_ci#ifdef DEBUG_LINK 18262306a36Sopenharmony_ci printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name, 18362306a36Sopenharmony_ci hdlc->carrier, hdlc->open); 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci spin_lock_irq(&hdlc->state_lock); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci hdlc->open = 0; 18962306a36Sopenharmony_ci if (hdlc->carrier) 19062306a36Sopenharmony_ci hdlc_proto_stop(dev); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci spin_unlock_irq(&hdlc->state_lock); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (hdlc->proto->close) 19562306a36Sopenharmony_ci hdlc->proto->close(dev); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ciEXPORT_SYMBOL(hdlc_close); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciint hdlc_ioctl(struct net_device *dev, struct if_settings *ifs) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct hdlc_proto *proto = first_proto; 20262306a36Sopenharmony_ci int result; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (dev_to_hdlc(dev)->proto) { 20562306a36Sopenharmony_ci result = dev_to_hdlc(dev)->proto->ioctl(dev, ifs); 20662306a36Sopenharmony_ci if (result != -EINVAL) 20762306a36Sopenharmony_ci return result; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Not handled by currently attached protocol (if any) */ 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci while (proto) { 21362306a36Sopenharmony_ci result = proto->ioctl(dev, ifs); 21462306a36Sopenharmony_ci if (result != -EINVAL) 21562306a36Sopenharmony_ci return result; 21662306a36Sopenharmony_ci proto = proto->next; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ciEXPORT_SYMBOL(hdlc_ioctl); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct header_ops hdlc_null_ops; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void hdlc_setup_dev(struct net_device *dev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci /* Re-init all variables changed by HDLC protocol drivers, 22762306a36Sopenharmony_ci * including ether_setup() called from hdlc_raw_eth.c. 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 23062306a36Sopenharmony_ci dev->priv_flags = IFF_WAN_HDLC; 23162306a36Sopenharmony_ci dev->mtu = HDLC_MAX_MTU; 23262306a36Sopenharmony_ci dev->min_mtu = 68; 23362306a36Sopenharmony_ci dev->max_mtu = HDLC_MAX_MTU; 23462306a36Sopenharmony_ci dev->type = ARPHRD_RAWHDLC; 23562306a36Sopenharmony_ci dev->hard_header_len = 0; 23662306a36Sopenharmony_ci dev->needed_headroom = 0; 23762306a36Sopenharmony_ci dev->addr_len = 0; 23862306a36Sopenharmony_ci dev->header_ops = &hdlc_null_ops; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void hdlc_setup(struct net_device *dev) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci hdlc_setup_dev(dev); 24662306a36Sopenharmony_ci hdlc->carrier = 1; 24762306a36Sopenharmony_ci hdlc->open = 0; 24862306a36Sopenharmony_ci spin_lock_init(&hdlc->state_lock); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistruct net_device *alloc_hdlcdev(void *priv) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct net_device *dev; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d", 25662306a36Sopenharmony_ci NET_NAME_UNKNOWN, hdlc_setup); 25762306a36Sopenharmony_ci if (dev) 25862306a36Sopenharmony_ci dev_to_hdlc(dev)->priv = priv; 25962306a36Sopenharmony_ci return dev; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ciEXPORT_SYMBOL(alloc_hdlcdev); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_civoid unregister_hdlc_device(struct net_device *dev) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci rtnl_lock(); 26662306a36Sopenharmony_ci detach_hdlc_protocol(dev); 26762306a36Sopenharmony_ci unregister_netdevice(dev); 26862306a36Sopenharmony_ci rtnl_unlock(); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_hdlc_device); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ciint attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, 27362306a36Sopenharmony_ci size_t size) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int err; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci err = detach_hdlc_protocol(dev); 27862306a36Sopenharmony_ci if (err) 27962306a36Sopenharmony_ci return err; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!try_module_get(proto->module)) 28262306a36Sopenharmony_ci return -ENOSYS; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (size) { 28562306a36Sopenharmony_ci dev_to_hdlc(dev)->state = kmalloc(size, GFP_KERNEL); 28662306a36Sopenharmony_ci if (!dev_to_hdlc(dev)->state) { 28762306a36Sopenharmony_ci module_put(proto->module); 28862306a36Sopenharmony_ci return -ENOBUFS; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci dev_to_hdlc(dev)->proto = proto; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ciEXPORT_SYMBOL(attach_hdlc_protocol); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciint detach_hdlc_protocol(struct net_device *dev) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci hdlc_device *hdlc = dev_to_hdlc(dev); 30062306a36Sopenharmony_ci int err; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (hdlc->proto) { 30362306a36Sopenharmony_ci err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev); 30462306a36Sopenharmony_ci err = notifier_to_errno(err); 30562306a36Sopenharmony_ci if (err) { 30662306a36Sopenharmony_ci netdev_err(dev, "Refused to change device type\n"); 30762306a36Sopenharmony_ci return err; 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (hdlc->proto->detach) 31162306a36Sopenharmony_ci hdlc->proto->detach(dev); 31262306a36Sopenharmony_ci module_put(hdlc->proto->module); 31362306a36Sopenharmony_ci hdlc->proto = NULL; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci kfree(hdlc->state); 31662306a36Sopenharmony_ci hdlc->state = NULL; 31762306a36Sopenharmony_ci hdlc_setup_dev(dev); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ciEXPORT_SYMBOL(detach_hdlc_protocol); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_civoid register_hdlc_protocol(struct hdlc_proto *proto) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci rtnl_lock(); 32662306a36Sopenharmony_ci proto->next = first_proto; 32762306a36Sopenharmony_ci first_proto = proto; 32862306a36Sopenharmony_ci rtnl_unlock(); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ciEXPORT_SYMBOL(register_hdlc_protocol); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_civoid unregister_hdlc_protocol(struct hdlc_proto *proto) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct hdlc_proto **p; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci rtnl_lock(); 33762306a36Sopenharmony_ci p = &first_proto; 33862306a36Sopenharmony_ci while (*p != proto) { 33962306a36Sopenharmony_ci BUG_ON(!*p); 34062306a36Sopenharmony_ci p = &((*p)->next); 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci *p = proto->next; 34362306a36Sopenharmony_ci rtnl_unlock(); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_hdlc_protocol); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); 34862306a36Sopenharmony_ciMODULE_DESCRIPTION("HDLC support module"); 34962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic struct packet_type hdlc_packet_type __read_mostly = { 35262306a36Sopenharmony_ci .type = cpu_to_be16(ETH_P_HDLC), 35362306a36Sopenharmony_ci .func = hdlc_rcv, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic struct notifier_block hdlc_notifier = { 35762306a36Sopenharmony_ci .notifier_call = hdlc_device_event, 35862306a36Sopenharmony_ci}; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int __init hdlc_module_init(void) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci int result; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci pr_info("%s\n", version); 36562306a36Sopenharmony_ci result = register_netdevice_notifier(&hdlc_notifier); 36662306a36Sopenharmony_ci if (result) 36762306a36Sopenharmony_ci return result; 36862306a36Sopenharmony_ci dev_add_pack(&hdlc_packet_type); 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void __exit hdlc_module_exit(void) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci dev_remove_pack(&hdlc_packet_type); 37562306a36Sopenharmony_ci unregister_netdevice_notifier(&hdlc_notifier); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cimodule_init(hdlc_module_init); 37962306a36Sopenharmony_cimodule_exit(hdlc_module_exit); 380