1/*
2 * Generic HDLC support routines for Linux
3 * HDLC Ethernet emulation support
4 *
5 * Copyright (C) 2002-2006 Krzysztof Halasa <khc@pm.waw.pl>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of version 2 of the GNU General Public License
9 * as published by the Free Software Foundation.
10 */
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/slab.h>
15#include <linux/poll.h>
16#include <linux/errno.h>
17#include <linux/if_arp.h>
18#include <linux/init.h>
19#include <linux/skbuff.h>
20#include <linux/pkt_sched.h>
21#include <linux/random.h>
22#include <linux/inetdevice.h>
23#include <linux/lapb.h>
24#include <linux/rtnetlink.h>
25#include <linux/etherdevice.h>
26#include <linux/hdlc.h>
27
28static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr);
29
30static int eth_tx(struct sk_buff *skb, struct net_device *dev)
31{
32	int pad = ETH_ZLEN - skb->len;
33	if (pad > 0) {		/* Pad the frame with zeros */
34		int len = skb->len;
35		if (skb_tailroom(skb) < pad)
36			if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) {
37				hdlc_stats(dev)->tx_dropped++;
38				dev_kfree_skb(skb);
39				return 0;
40			}
41		skb_put(skb, pad);
42		memset(skb->data + len, 0, pad);
43	}
44	return dev_to_hdlc(dev)->xmit(skb, dev);
45}
46
47
48static struct hdlc_proto proto = {
49	.type_trans	= eth_type_trans,
50	.ioctl		= raw_eth_ioctl,
51	.module		= THIS_MODULE,
52};
53
54
55static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
56{
57	raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
58	const size_t size = sizeof(raw_hdlc_proto);
59	raw_hdlc_proto new_settings;
60	hdlc_device *hdlc = dev_to_hdlc(dev);
61	int result;
62	void *old_ch_mtu;
63	int old_qlen;
64
65	switch (ifr->ifr_settings.type) {
66	case IF_GET_PROTO:
67		if (dev_to_hdlc(dev)->proto != &proto)
68			return -EINVAL;
69		ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
70		if (ifr->ifr_settings.size < size) {
71			ifr->ifr_settings.size = size; /* data size wanted */
72			return -ENOBUFS;
73		}
74		if (copy_to_user(raw_s, hdlc->state, size))
75			return -EFAULT;
76		return 0;
77
78	case IF_PROTO_HDLC_ETH:
79		if (!capable(CAP_NET_ADMIN))
80			return -EPERM;
81
82		if (dev->flags & IFF_UP)
83			return -EBUSY;
84
85		if (copy_from_user(&new_settings, raw_s, size))
86			return -EFAULT;
87
88		if (new_settings.encoding == ENCODING_DEFAULT)
89			new_settings.encoding = ENCODING_NRZ;
90
91		if (new_settings.parity == PARITY_DEFAULT)
92			new_settings.parity = PARITY_CRC16_PR1_CCITT;
93
94		result = hdlc->attach(dev, new_settings.encoding,
95				      new_settings.parity);
96		if (result)
97			return result;
98
99		result = attach_hdlc_protocol(dev, &proto, NULL,
100					      sizeof(raw_hdlc_proto));
101		if (result)
102			return result;
103		memcpy(hdlc->state, &new_settings, size);
104		dev->hard_start_xmit = eth_tx;
105		old_ch_mtu = dev->change_mtu;
106		old_qlen = dev->tx_queue_len;
107		ether_setup(dev);
108		dev->change_mtu = old_ch_mtu;
109		dev->tx_queue_len = old_qlen;
110		memcpy(dev->dev_addr, "\x00\x01", 2);
111                get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
112		netif_dormant_off(dev);
113		return 0;
114	}
115
116	return -EINVAL;
117}
118
119
120static int __init mod_init(void)
121{
122	register_hdlc_protocol(&proto);
123	return 0;
124}
125
126
127
128static void __exit mod_exit(void)
129{
130	unregister_hdlc_protocol(&proto);
131}
132
133
134module_init(mod_init);
135module_exit(mod_exit);
136
137MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
138MODULE_DESCRIPTION("Ethernet encapsulation support for generic HDLC");
139MODULE_LICENSE("GPL v2");
140