hdlc_x25.c revision 198191c4a7ce4daba379608fb38b9bc5a4eedc61
1/*
2 * Generic HDLC support routines for Linux
3 * X.25 support
4 *
5 * Copyright (C) 1999 - 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/inetdevice.h>
22#include <linux/lapb.h>
23#include <linux/rtnetlink.h>
24#include <linux/hdlc.h>
25
26#include <net/x25device.h>
27
28static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
29
30/* These functions are callbacks called by LAPB layer */
31
32static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
33{
34	struct sk_buff *skb;
35	unsigned char *ptr;
36
37	if ((skb = dev_alloc_skb(1)) == NULL) {
38		printk(KERN_ERR "%s: out of memory\n", dev->name);
39		return;
40	}
41
42	ptr = skb_put(skb, 1);
43	*ptr = code;
44
45	skb->protocol = x25_type_trans(skb, dev);
46	netif_rx(skb);
47}
48
49
50
51static void x25_connected(struct net_device *dev, int reason)
52{
53	x25_connect_disconnect(dev, reason, 1);
54}
55
56
57
58static void x25_disconnected(struct net_device *dev, int reason)
59{
60	x25_connect_disconnect(dev, reason, 2);
61}
62
63
64
65static int x25_data_indication(struct net_device *dev, struct sk_buff *skb)
66{
67	unsigned char *ptr;
68
69	skb_push(skb, 1);
70
71	if (skb_cow(skb, 1))
72		return NET_RX_DROP;
73
74	ptr  = skb->data;
75	*ptr = 0;
76
77	skb->protocol = x25_type_trans(skb, dev);
78	return netif_rx(skb);
79}
80
81
82
83static void x25_data_transmit(struct net_device *dev, struct sk_buff *skb)
84{
85	hdlc_device *hdlc = dev_to_hdlc(dev);
86	hdlc->xmit(skb, dev); /* Ignore return value :-( */
87}
88
89
90
91static int x25_xmit(struct sk_buff *skb, struct net_device *dev)
92{
93	int result;
94
95
96	/* X.25 to LAPB */
97	switch (skb->data[0]) {
98	case 0:		/* Data to be transmitted */
99		skb_pull(skb, 1);
100		if ((result = lapb_data_request(dev, skb)) != LAPB_OK)
101			dev_kfree_skb(skb);
102		return 0;
103
104	case 1:
105		if ((result = lapb_connect_request(dev))!= LAPB_OK) {
106			if (result == LAPB_CONNECTED)
107				/* Send connect confirm. msg to level 3 */
108				x25_connected(dev, 0);
109			else
110				printk(KERN_ERR "%s: LAPB connect request "
111				       "failed, error code = %i\n",
112				       dev->name, result);
113		}
114		break;
115
116	case 2:
117		if ((result = lapb_disconnect_request(dev)) != LAPB_OK) {
118			if (result == LAPB_NOTCONNECTED)
119				/* Send disconnect confirm. msg to level 3 */
120				x25_disconnected(dev, 0);
121			else
122				printk(KERN_ERR "%s: LAPB disconnect request "
123				       "failed, error code = %i\n",
124				       dev->name, result);
125		}
126		break;
127
128	default:		/* to be defined */
129		break;
130	}
131
132	dev_kfree_skb(skb);
133	return 0;
134}
135
136
137
138static int x25_open(struct net_device *dev)
139{
140	struct lapb_register_struct cb;
141	int result;
142
143	cb.connect_confirmation = x25_connected;
144	cb.connect_indication = x25_connected;
145	cb.disconnect_confirmation = x25_disconnected;
146	cb.disconnect_indication = x25_disconnected;
147	cb.data_indication = x25_data_indication;
148	cb.data_transmit = x25_data_transmit;
149
150	result = lapb_register(dev, &cb);
151	if (result != LAPB_OK)
152		return result;
153	return 0;
154}
155
156
157
158static void x25_close(struct net_device *dev)
159{
160	lapb_unregister(dev);
161}
162
163
164
165static int x25_rx(struct sk_buff *skb)
166{
167	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
168		skb->dev->stats.rx_dropped++;
169		return NET_RX_DROP;
170	}
171
172	if (lapb_data_received(skb->dev, skb) == LAPB_OK)
173		return NET_RX_SUCCESS;
174
175	skb->dev->stats.rx_errors++;
176	dev_kfree_skb_any(skb);
177	return NET_RX_DROP;
178}
179
180
181static struct hdlc_proto proto = {
182	.open		= x25_open,
183	.close		= x25_close,
184	.ioctl		= x25_ioctl,
185	.netif_rx	= x25_rx,
186	.module		= THIS_MODULE,
187};
188
189
190static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
191{
192	hdlc_device *hdlc = dev_to_hdlc(dev);
193	int result;
194
195	switch (ifr->ifr_settings.type) {
196	case IF_GET_PROTO:
197		if (dev_to_hdlc(dev)->proto != &proto)
198			return -EINVAL;
199		ifr->ifr_settings.type = IF_PROTO_X25;
200		return 0; /* return protocol only, no settable parameters */
201
202	case IF_PROTO_X25:
203		if(!capable(CAP_NET_ADMIN))
204			return -EPERM;
205
206		if(dev->flags & IFF_UP)
207			return -EBUSY;
208
209		result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
210		if (result)
211			return result;
212
213		if ((result = attach_hdlc_protocol(dev, &proto, 0)))
214			return result;
215		dev->hard_start_xmit = x25_xmit;
216		dev->type = ARPHRD_X25;
217		netif_dormant_off(dev);
218		return 0;
219	}
220
221	return -EINVAL;
222}
223
224
225static int __init mod_init(void)
226{
227	register_hdlc_protocol(&proto);
228	return 0;
229}
230
231
232
233static void __exit mod_exit(void)
234{
235	unregister_hdlc_protocol(&proto);
236}
237
238
239module_init(mod_init);
240module_exit(mod_exit);
241
242MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
243MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
244MODULE_LICENSE("GPL v2");
245