blob: a63f6a2cc4f7d75746a8ba3869c7a62d649fad91 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Generic HDLC support routines for Linux
3 *
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +02004 * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
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/config.h>
26#include <linux/module.h>
27#include <linux/kernel.h>
28#include <linux/slab.h>
29#include <linux/poll.h>
30#include <linux/errno.h>
31#include <linux/if_arp.h>
32#include <linux/init.h>
33#include <linux/skbuff.h>
34#include <linux/pkt_sched.h>
35#include <linux/inetdevice.h>
36#include <linux/lapb.h>
37#include <linux/rtnetlink.h>
38#include <linux/hdlc.h>
39
40
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +020041static const char* version = "HDLC support module revision 1.18";
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
43#undef DEBUG_LINK
44
45
46static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
47{
48 if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
49 return -EINVAL;
50 dev->mtu = new_mtu;
51 return 0;
52}
53
54
55
56static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
57{
58 return hdlc_stats(dev);
59}
60
61
62
63static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
64 struct packet_type *p)
65{
66 hdlc_device *hdlc = dev_to_hdlc(dev);
67 if (hdlc->proto.netif_rx)
68 return hdlc->proto.netif_rx(skb);
69
70 hdlc->stats.rx_dropped++; /* Shouldn't happen */
71 dev_kfree_skb(skb);
72 return NET_RX_DROP;
73}
74
75
76
77static void __hdlc_set_carrier_on(struct net_device *dev)
78{
79 hdlc_device *hdlc = dev_to_hdlc(dev);
80 if (hdlc->proto.start)
81 return hdlc->proto.start(dev);
82#ifdef DEBUG_LINK
83 if (netif_carrier_ok(dev))
84 printk(KERN_ERR "hdlc_set_carrier_on(): already on\n");
85#endif
86 netif_carrier_on(dev);
87}
88
89
90
91static void __hdlc_set_carrier_off(struct net_device *dev)
92{
93 hdlc_device *hdlc = dev_to_hdlc(dev);
94 if (hdlc->proto.stop)
95 return hdlc->proto.stop(dev);
96
97#ifdef DEBUG_LINK
98 if (!netif_carrier_ok(dev))
99 printk(KERN_ERR "hdlc_set_carrier_off(): already off\n");
100#endif
101 netif_carrier_off(dev);
102}
103
104
105
106void hdlc_set_carrier(int on, struct net_device *dev)
107{
108 hdlc_device *hdlc = dev_to_hdlc(dev);
109 unsigned long flags;
110 on = on ? 1 : 0;
111
112#ifdef DEBUG_LINK
113 printk(KERN_DEBUG "hdlc_set_carrier %i\n", on);
114#endif
115
116 spin_lock_irqsave(&hdlc->state_lock, flags);
117
118 if (hdlc->carrier == on)
119 goto carrier_exit; /* no change in DCD line level */
120
121#ifdef DEBUG_LINK
122 printk(KERN_INFO "%s: carrier %s\n", dev->name, on ? "ON" : "off");
123#endif
124 hdlc->carrier = on;
125
126 if (!hdlc->open)
127 goto carrier_exit;
128
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +0200129 if (hdlc->carrier) {
130 printk(KERN_INFO "%s: Carrier detected\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 __hdlc_set_carrier_on(dev);
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +0200132 } else {
133 printk(KERN_INFO "%s: Carrier lost\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 __hdlc_set_carrier_off(dev);
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +0200135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137carrier_exit:
138 spin_unlock_irqrestore(&hdlc->state_lock, flags);
139}
140
141
142
143/* Must be called by hardware driver when HDLC device is being opened */
144int hdlc_open(struct net_device *dev)
145{
146 hdlc_device *hdlc = dev_to_hdlc(dev);
147#ifdef DEBUG_LINK
148 printk(KERN_DEBUG "hdlc_open() carrier %i open %i\n",
149 hdlc->carrier, hdlc->open);
150#endif
151
152 if (hdlc->proto.id == -1)
153 return -ENOSYS; /* no protocol attached */
154
155 if (hdlc->proto.open) {
156 int result = hdlc->proto.open(dev);
157 if (result)
158 return result;
159 }
160
161 spin_lock_irq(&hdlc->state_lock);
162
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +0200163 if (hdlc->carrier) {
164 printk(KERN_INFO "%s: Carrier detected\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 __hdlc_set_carrier_on(dev);
Krzysztof Halasab3dd65f2005-04-21 15:57:25 +0200166 } else
167 printk(KERN_INFO "%s: No carrier\n", dev->name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
169 hdlc->open = 1;
170
171 spin_unlock_irq(&hdlc->state_lock);
172 return 0;
173}
174
175
176
177/* Must be called by hardware driver when HDLC device is being closed */
178void hdlc_close(struct net_device *dev)
179{
180 hdlc_device *hdlc = dev_to_hdlc(dev);
181#ifdef DEBUG_LINK
182 printk(KERN_DEBUG "hdlc_close() carrier %i open %i\n",
183 hdlc->carrier, hdlc->open);
184#endif
185
186 spin_lock_irq(&hdlc->state_lock);
187
188 hdlc->open = 0;
189 if (hdlc->carrier)
190 __hdlc_set_carrier_off(dev);
191
192 spin_unlock_irq(&hdlc->state_lock);
193
194 if (hdlc->proto.close)
195 hdlc->proto.close(dev);
196}
197
198
199
200#ifndef CONFIG_HDLC_RAW
201#define hdlc_raw_ioctl(dev, ifr) -ENOSYS
202#endif
203
204#ifndef CONFIG_HDLC_RAW_ETH
205#define hdlc_raw_eth_ioctl(dev, ifr) -ENOSYS
206#endif
207
208#ifndef CONFIG_HDLC_PPP
209#define hdlc_ppp_ioctl(dev, ifr) -ENOSYS
210#endif
211
212#ifndef CONFIG_HDLC_CISCO
213#define hdlc_cisco_ioctl(dev, ifr) -ENOSYS
214#endif
215
216#ifndef CONFIG_HDLC_FR
217#define hdlc_fr_ioctl(dev, ifr) -ENOSYS
218#endif
219
220#ifndef CONFIG_HDLC_X25
221#define hdlc_x25_ioctl(dev, ifr) -ENOSYS
222#endif
223
224
225int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
226{
227 hdlc_device *hdlc = dev_to_hdlc(dev);
228 unsigned int proto;
229
230 if (cmd != SIOCWANDEV)
231 return -EINVAL;
232
233 switch(ifr->ifr_settings.type) {
234 case IF_PROTO_HDLC:
235 case IF_PROTO_HDLC_ETH:
236 case IF_PROTO_PPP:
237 case IF_PROTO_CISCO:
238 case IF_PROTO_FR:
239 case IF_PROTO_X25:
240 proto = ifr->ifr_settings.type;
241 break;
242
243 default:
244 proto = hdlc->proto.id;
245 }
246
247 switch(proto) {
248 case IF_PROTO_HDLC: return hdlc_raw_ioctl(dev, ifr);
249 case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(dev, ifr);
250 case IF_PROTO_PPP: return hdlc_ppp_ioctl(dev, ifr);
251 case IF_PROTO_CISCO: return hdlc_cisco_ioctl(dev, ifr);
252 case IF_PROTO_FR: return hdlc_fr_ioctl(dev, ifr);
253 case IF_PROTO_X25: return hdlc_x25_ioctl(dev, ifr);
254 default: return -EINVAL;
255 }
256}
257
258static void hdlc_setup(struct net_device *dev)
259{
260 hdlc_device *hdlc = dev_to_hdlc(dev);
261
262 dev->get_stats = hdlc_get_stats;
263 dev->change_mtu = hdlc_change_mtu;
264 dev->mtu = HDLC_MAX_MTU;
265
266 dev->type = ARPHRD_RAWHDLC;
267 dev->hard_header_len = 16;
268
269 dev->flags = IFF_POINTOPOINT | IFF_NOARP;
270
271 hdlc->proto.id = -1;
272 hdlc->proto.detach = NULL;
273 hdlc->carrier = 1;
274 hdlc->open = 0;
275 spin_lock_init(&hdlc->state_lock);
276}
277
278struct net_device *alloc_hdlcdev(void *priv)
279{
280 struct net_device *dev;
281 dev = alloc_netdev(sizeof(hdlc_device), "hdlc%d", hdlc_setup);
282 if (dev)
283 dev_to_hdlc(dev)->priv = priv;
284 return dev;
285}
286
287int register_hdlc_device(struct net_device *dev)
288{
289 int result = dev_alloc_name(dev, "hdlc%d");
290 if (result < 0)
291 return result;
292
293 result = register_netdev(dev);
294 if (result != 0)
295 return -EIO;
296
297 if (netif_carrier_ok(dev))
298 netif_carrier_off(dev); /* no carrier until DCD goes up */
299
300 return 0;
301}
302
303
304
305void unregister_hdlc_device(struct net_device *dev)
306{
307 rtnl_lock();
308 hdlc_proto_detach(dev_to_hdlc(dev));
309 unregister_netdevice(dev);
310 rtnl_unlock();
311}
312
313
314
315MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
316MODULE_DESCRIPTION("HDLC support module");
317MODULE_LICENSE("GPL v2");
318
319EXPORT_SYMBOL(hdlc_open);
320EXPORT_SYMBOL(hdlc_close);
321EXPORT_SYMBOL(hdlc_set_carrier);
322EXPORT_SYMBOL(hdlc_ioctl);
323EXPORT_SYMBOL(alloc_hdlcdev);
324EXPORT_SYMBOL(register_hdlc_device);
325EXPORT_SYMBOL(unregister_hdlc_device);
326
327static struct packet_type hdlc_packet_type = {
328 .type = __constant_htons(ETH_P_HDLC),
329 .func = hdlc_rcv,
330};
331
332
333static int __init hdlc_module_init(void)
334{
335 printk(KERN_INFO "%s\n", version);
336 dev_add_pack(&hdlc_packet_type);
337 return 0;
338}
339
340
341
342static void __exit hdlc_module_exit(void)
343{
344 dev_remove_pack(&hdlc_packet_type);
345}
346
347
348module_init(hdlc_module_init);
349module_exit(hdlc_module_exit);