18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Generic HDLC support routines for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Currently supported:
88c2ecf20Sopenharmony_ci *	* raw IP-in-HDLC
98c2ecf20Sopenharmony_ci *	* Cisco HDLC
108c2ecf20Sopenharmony_ci *	* Frame Relay with ANSI or CCITT LMI (both user and network side)
118c2ecf20Sopenharmony_ci *	* PPP
128c2ecf20Sopenharmony_ci *	* X.25
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Use sethdlc utility to set line parameters, protocol and PVCs
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * How does it work:
178c2ecf20Sopenharmony_ci * - proto->open(), close(), start(), stop() calls are serialized.
188c2ecf20Sopenharmony_ci *   The order is: open, [ start, stop ... ] close ...
198c2ecf20Sopenharmony_ci * - proto->start() and stop() are called with spin_lock_irq held.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/hdlc.h>
268c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
278c2ecf20Sopenharmony_ci#include <linux/inetdevice.h>
288c2ecf20Sopenharmony_ci#include <linux/init.h>
298c2ecf20Sopenharmony_ci#include <linux/kernel.h>
308c2ecf20Sopenharmony_ci#include <linux/module.h>
318c2ecf20Sopenharmony_ci#include <linux/notifier.h>
328c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h>
338c2ecf20Sopenharmony_ci#include <linux/poll.h>
348c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
358c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
368c2ecf20Sopenharmony_ci#include <linux/slab.h>
378c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic const char* version = "HDLC support module revision 1.22";
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#undef DEBUG_LINK
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic struct hdlc_proto *first_proto;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
478c2ecf20Sopenharmony_ci		    struct packet_type *p, struct net_device *orig_dev)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct hdlc_device *hdlc;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* First make sure "dev" is an HDLC device */
528c2ecf20Sopenharmony_ci	if (!(dev->priv_flags & IFF_WAN_HDLC)) {
538c2ecf20Sopenharmony_ci		kfree_skb(skb);
548c2ecf20Sopenharmony_ci		return NET_RX_SUCCESS;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	hdlc = dev_to_hdlc(dev);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net)) {
608c2ecf20Sopenharmony_ci		kfree_skb(skb);
618c2ecf20Sopenharmony_ci		return 0;
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	BUG_ON(!hdlc->proto->netif_rx);
658c2ecf20Sopenharmony_ci	return hdlc->proto->netif_rx(skb);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cinetdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (hdlc->proto->xmit)
738c2ecf20Sopenharmony_ci		return hdlc->proto->xmit(skb, dev);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return hdlc->xmit(skb, dev); /* call hardware driver directly */
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic inline void hdlc_proto_start(struct net_device *dev)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
818c2ecf20Sopenharmony_ci	if (hdlc->proto->start)
828c2ecf20Sopenharmony_ci		hdlc->proto->start(dev);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic inline void hdlc_proto_stop(struct net_device *dev)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
908c2ecf20Sopenharmony_ci	if (hdlc->proto->stop)
918c2ecf20Sopenharmony_ci		hdlc->proto->stop(dev);
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int hdlc_device_event(struct notifier_block *this, unsigned long event,
978c2ecf20Sopenharmony_ci			     void *ptr)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1008c2ecf20Sopenharmony_ci	hdlc_device *hdlc;
1018c2ecf20Sopenharmony_ci	unsigned long flags;
1028c2ecf20Sopenharmony_ci	int on;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
1058c2ecf20Sopenharmony_ci		return NOTIFY_DONE;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!(dev->priv_flags & IFF_WAN_HDLC))
1088c2ecf20Sopenharmony_ci		return NOTIFY_DONE; /* not an HDLC device */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (event != NETDEV_CHANGE)
1118c2ecf20Sopenharmony_ci		return NOTIFY_DONE; /* Only interested in carrier changes */
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	on = netif_carrier_ok(dev);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#ifdef DEBUG_LINK
1168c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s: hdlc_device_event NETDEV_CHANGE, carrier %i\n",
1178c2ecf20Sopenharmony_ci	       dev->name, on);
1188c2ecf20Sopenharmony_ci#endif
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	hdlc = dev_to_hdlc(dev);
1218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hdlc->state_lock, flags);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (hdlc->carrier == on)
1248c2ecf20Sopenharmony_ci		goto carrier_exit; /* no change in DCD line level */
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	hdlc->carrier = on;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (!hdlc->open)
1298c2ecf20Sopenharmony_ci		goto carrier_exit;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (hdlc->carrier) {
1328c2ecf20Sopenharmony_ci		netdev_info(dev, "Carrier detected\n");
1338c2ecf20Sopenharmony_ci		hdlc_proto_start(dev);
1348c2ecf20Sopenharmony_ci	} else {
1358c2ecf20Sopenharmony_ci		netdev_info(dev, "Carrier lost\n");
1368c2ecf20Sopenharmony_ci		hdlc_proto_stop(dev);
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cicarrier_exit:
1408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hdlc->state_lock, flags);
1418c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci/* Must be called by hardware driver when HDLC device is being opened */
1478c2ecf20Sopenharmony_ciint hdlc_open(struct net_device *dev)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
1508c2ecf20Sopenharmony_ci#ifdef DEBUG_LINK
1518c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name,
1528c2ecf20Sopenharmony_ci	       hdlc->carrier, hdlc->open);
1538c2ecf20Sopenharmony_ci#endif
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (hdlc->proto == NULL)
1568c2ecf20Sopenharmony_ci		return -ENOSYS;	/* no protocol attached */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (hdlc->proto->open) {
1598c2ecf20Sopenharmony_ci		int result = hdlc->proto->open(dev);
1608c2ecf20Sopenharmony_ci		if (result)
1618c2ecf20Sopenharmony_ci			return result;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	spin_lock_irq(&hdlc->state_lock);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	if (hdlc->carrier) {
1678c2ecf20Sopenharmony_ci		netdev_info(dev, "Carrier detected\n");
1688c2ecf20Sopenharmony_ci		hdlc_proto_start(dev);
1698c2ecf20Sopenharmony_ci	} else
1708c2ecf20Sopenharmony_ci		netdev_info(dev, "No carrier\n");
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	hdlc->open = 1;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	spin_unlock_irq(&hdlc->state_lock);
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci/* Must be called by hardware driver when HDLC device is being closed */
1818c2ecf20Sopenharmony_civoid hdlc_close(struct net_device *dev)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
1848c2ecf20Sopenharmony_ci#ifdef DEBUG_LINK
1858c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name,
1868c2ecf20Sopenharmony_ci	       hdlc->carrier, hdlc->open);
1878c2ecf20Sopenharmony_ci#endif
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	spin_lock_irq(&hdlc->state_lock);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	hdlc->open = 0;
1928c2ecf20Sopenharmony_ci	if (hdlc->carrier)
1938c2ecf20Sopenharmony_ci		hdlc_proto_stop(dev);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	spin_unlock_irq(&hdlc->state_lock);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	if (hdlc->proto->close)
1988c2ecf20Sopenharmony_ci		hdlc->proto->close(dev);
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciint hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct hdlc_proto *proto = first_proto;
2068c2ecf20Sopenharmony_ci	int result;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (cmd != SIOCWANDEV)
2098c2ecf20Sopenharmony_ci		return -EINVAL;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (dev_to_hdlc(dev)->proto) {
2128c2ecf20Sopenharmony_ci		result = dev_to_hdlc(dev)->proto->ioctl(dev, ifr);
2138c2ecf20Sopenharmony_ci		if (result != -EINVAL)
2148c2ecf20Sopenharmony_ci			return result;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Not handled by currently attached protocol (if any) */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	while (proto) {
2208c2ecf20Sopenharmony_ci		if ((result = proto->ioctl(dev, ifr)) != -EINVAL)
2218c2ecf20Sopenharmony_ci			return result;
2228c2ecf20Sopenharmony_ci		proto = proto->next;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci	return -EINVAL;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic const struct header_ops hdlc_null_ops;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void hdlc_setup_dev(struct net_device *dev)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	/* Re-init all variables changed by HDLC protocol drivers,
2328c2ecf20Sopenharmony_ci	 * including ether_setup() called from hdlc_raw_eth.c.
2338c2ecf20Sopenharmony_ci	 */
2348c2ecf20Sopenharmony_ci	dev->flags		 = IFF_POINTOPOINT | IFF_NOARP;
2358c2ecf20Sopenharmony_ci	dev->priv_flags		 = IFF_WAN_HDLC;
2368c2ecf20Sopenharmony_ci	dev->mtu		 = HDLC_MAX_MTU;
2378c2ecf20Sopenharmony_ci	dev->min_mtu		 = 68;
2388c2ecf20Sopenharmony_ci	dev->max_mtu		 = HDLC_MAX_MTU;
2398c2ecf20Sopenharmony_ci	dev->type		 = ARPHRD_RAWHDLC;
2408c2ecf20Sopenharmony_ci	dev->hard_header_len	 = 0;
2418c2ecf20Sopenharmony_ci	dev->needed_headroom	 = 0;
2428c2ecf20Sopenharmony_ci	dev->addr_len		 = 0;
2438c2ecf20Sopenharmony_ci	dev->header_ops		 = &hdlc_null_ops;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic void hdlc_setup(struct net_device *dev)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	hdlc_setup_dev(dev);
2518c2ecf20Sopenharmony_ci	hdlc->carrier = 1;
2528c2ecf20Sopenharmony_ci	hdlc->open = 0;
2538c2ecf20Sopenharmony_ci	spin_lock_init(&hdlc->state_lock);
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistruct net_device *alloc_hdlcdev(void *priv)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	struct net_device *dev;
2598c2ecf20Sopenharmony_ci	dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d",
2608c2ecf20Sopenharmony_ci			   NET_NAME_UNKNOWN, hdlc_setup);
2618c2ecf20Sopenharmony_ci	if (dev)
2628c2ecf20Sopenharmony_ci		dev_to_hdlc(dev)->priv = priv;
2638c2ecf20Sopenharmony_ci	return dev;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_civoid unregister_hdlc_device(struct net_device *dev)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	rtnl_lock();
2698c2ecf20Sopenharmony_ci	detach_hdlc_protocol(dev);
2708c2ecf20Sopenharmony_ci	unregister_netdevice(dev);
2718c2ecf20Sopenharmony_ci	rtnl_unlock();
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ciint attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
2778c2ecf20Sopenharmony_ci			 size_t size)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	int err;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	err = detach_hdlc_protocol(dev);
2828c2ecf20Sopenharmony_ci	if (err)
2838c2ecf20Sopenharmony_ci		return err;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (!try_module_get(proto->module))
2868c2ecf20Sopenharmony_ci		return -ENOSYS;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	if (size) {
2898c2ecf20Sopenharmony_ci		dev_to_hdlc(dev)->state = kmalloc(size, GFP_KERNEL);
2908c2ecf20Sopenharmony_ci		if (dev_to_hdlc(dev)->state == NULL) {
2918c2ecf20Sopenharmony_ci			module_put(proto->module);
2928c2ecf20Sopenharmony_ci			return -ENOBUFS;
2938c2ecf20Sopenharmony_ci		}
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci	dev_to_hdlc(dev)->proto = proto;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ciint detach_hdlc_protocol(struct net_device *dev)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	hdlc_device *hdlc = dev_to_hdlc(dev);
3048c2ecf20Sopenharmony_ci	int err;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (hdlc->proto) {
3078c2ecf20Sopenharmony_ci		err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
3088c2ecf20Sopenharmony_ci		err = notifier_to_errno(err);
3098c2ecf20Sopenharmony_ci		if (err) {
3108c2ecf20Sopenharmony_ci			netdev_err(dev, "Refused to change device type\n");
3118c2ecf20Sopenharmony_ci			return err;
3128c2ecf20Sopenharmony_ci		}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci		if (hdlc->proto->detach)
3158c2ecf20Sopenharmony_ci			hdlc->proto->detach(dev);
3168c2ecf20Sopenharmony_ci		module_put(hdlc->proto->module);
3178c2ecf20Sopenharmony_ci		hdlc->proto = NULL;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	kfree(hdlc->state);
3208c2ecf20Sopenharmony_ci	hdlc->state = NULL;
3218c2ecf20Sopenharmony_ci	hdlc_setup_dev(dev);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	return 0;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_civoid register_hdlc_protocol(struct hdlc_proto *proto)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	rtnl_lock();
3308c2ecf20Sopenharmony_ci	proto->next = first_proto;
3318c2ecf20Sopenharmony_ci	first_proto = proto;
3328c2ecf20Sopenharmony_ci	rtnl_unlock();
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_civoid unregister_hdlc_protocol(struct hdlc_proto *proto)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct hdlc_proto **p;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	rtnl_lock();
3418c2ecf20Sopenharmony_ci	p = &first_proto;
3428c2ecf20Sopenharmony_ci	while (*p != proto) {
3438c2ecf20Sopenharmony_ci		BUG_ON(!*p);
3448c2ecf20Sopenharmony_ci		p = &((*p)->next);
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci	*p = proto->next;
3478c2ecf20Sopenharmony_ci	rtnl_unlock();
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ciMODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
3538c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HDLC support module");
3548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hdlc_start_xmit);
3578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hdlc_open);
3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hdlc_close);
3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hdlc_ioctl);
3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(alloc_hdlcdev);
3618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_hdlc_device);
3628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_hdlc_protocol);
3638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_hdlc_protocol);
3648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(attach_hdlc_protocol);
3658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(detach_hdlc_protocol);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic struct packet_type hdlc_packet_type __read_mostly = {
3688c2ecf20Sopenharmony_ci	.type = cpu_to_be16(ETH_P_HDLC),
3698c2ecf20Sopenharmony_ci	.func = hdlc_rcv,
3708c2ecf20Sopenharmony_ci};
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic struct notifier_block hdlc_notifier = {
3748c2ecf20Sopenharmony_ci	.notifier_call = hdlc_device_event,
3758c2ecf20Sopenharmony_ci};
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic int __init hdlc_module_init(void)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	int result;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	pr_info("%s\n", version);
3838c2ecf20Sopenharmony_ci	if ((result = register_netdevice_notifier(&hdlc_notifier)) != 0)
3848c2ecf20Sopenharmony_ci		return result;
3858c2ecf20Sopenharmony_ci	dev_add_pack(&hdlc_packet_type);
3868c2ecf20Sopenharmony_ci	return 0;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic void __exit hdlc_module_exit(void)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	dev_remove_pack(&hdlc_packet_type);
3948c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&hdlc_notifier);
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cimodule_init(hdlc_module_init);
3998c2ecf20Sopenharmony_cimodule_exit(hdlc_module_exit);
400