diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/wan/lapbether.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/wan/lapbether.c')
-rw-r--r-- | drivers/net/wan/lapbether.c | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c new file mode 100644 index 000000000000..7f2e3653c5e5 --- /dev/null +++ b/drivers/net/wan/lapbether.c | |||
@@ -0,0 +1,465 @@ | |||
1 | /* | ||
2 | * "LAPB via ethernet" driver release 001 | ||
3 | * | ||
4 | * This code REQUIRES 2.1.15 or higher/ NET3.038 | ||
5 | * | ||
6 | * This module: | ||
7 | * This module is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This is a "pseudo" network driver to allow LAPB over Ethernet. | ||
13 | * | ||
14 | * This driver can use any ethernet destination address, and can be | ||
15 | * limited to accept frames from one dedicated ethernet card only. | ||
16 | * | ||
17 | * History | ||
18 | * LAPBETH 001 Jonathan Naylor Cloned from bpqether.c | ||
19 | * 2000-10-29 Henner Eisen lapb_data_indication() return status. | ||
20 | * 2000-11-14 Henner Eisen dev_hold/put, NETDEV_GOING_DOWN support | ||
21 | */ | ||
22 | |||
23 | #include <linux/errno.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/socket.h> | ||
26 | #include <linux/in.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <linux/net.h> | ||
30 | #include <linux/inet.h> | ||
31 | #include <linux/netdevice.h> | ||
32 | #include <linux/if_arp.h> | ||
33 | #include <linux/skbuff.h> | ||
34 | #include <net/sock.h> | ||
35 | #include <asm/system.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #include <linux/mm.h> | ||
38 | #include <linux/interrupt.h> | ||
39 | #include <linux/notifier.h> | ||
40 | #include <linux/stat.h> | ||
41 | #include <linux/netfilter.h> | ||
42 | #include <linux/module.h> | ||
43 | #include <linux/lapb.h> | ||
44 | #include <linux/init.h> | ||
45 | |||
46 | #include <net/x25device.h> | ||
47 | |||
48 | static char bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | ||
49 | |||
50 | /* If this number is made larger, check that the temporary string buffer | ||
51 | * in lapbeth_new_device is large enough to store the probe device name.*/ | ||
52 | #define MAXLAPBDEV 100 | ||
53 | |||
54 | struct lapbethdev { | ||
55 | struct list_head node; | ||
56 | struct net_device *ethdev; /* link to ethernet device */ | ||
57 | struct net_device *axdev; /* lapbeth device (lapb#) */ | ||
58 | struct net_device_stats stats; /* some statistics */ | ||
59 | }; | ||
60 | |||
61 | static struct list_head lapbeth_devices = LIST_HEAD_INIT(lapbeth_devices); | ||
62 | |||
63 | /* ------------------------------------------------------------------------ */ | ||
64 | |||
65 | /* | ||
66 | * Get the LAPB device for the ethernet device | ||
67 | */ | ||
68 | static struct lapbethdev *lapbeth_get_x25_dev(struct net_device *dev) | ||
69 | { | ||
70 | struct lapbethdev *lapbeth; | ||
71 | |||
72 | list_for_each_entry_rcu(lapbeth, &lapbeth_devices, node) { | ||
73 | if (lapbeth->ethdev == dev) | ||
74 | return lapbeth; | ||
75 | } | ||
76 | return NULL; | ||
77 | } | ||
78 | |||
79 | static __inline__ int dev_is_ethdev(struct net_device *dev) | ||
80 | { | ||
81 | return dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5); | ||
82 | } | ||
83 | |||
84 | /* ------------------------------------------------------------------------ */ | ||
85 | |||
86 | /* | ||
87 | * Receive a LAPB frame via an ethernet interface. | ||
88 | */ | ||
89 | static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) | ||
90 | { | ||
91 | int len, err; | ||
92 | struct lapbethdev *lapbeth; | ||
93 | |||
94 | if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) | ||
95 | return NET_RX_DROP; | ||
96 | |||
97 | if (!pskb_may_pull(skb, 2)) | ||
98 | goto drop; | ||
99 | |||
100 | rcu_read_lock(); | ||
101 | lapbeth = lapbeth_get_x25_dev(dev); | ||
102 | if (!lapbeth) | ||
103 | goto drop_unlock; | ||
104 | if (!netif_running(lapbeth->axdev)) | ||
105 | goto drop_unlock; | ||
106 | |||
107 | lapbeth->stats.rx_packets++; | ||
108 | |||
109 | len = skb->data[0] + skb->data[1] * 256; | ||
110 | lapbeth->stats.rx_bytes += len; | ||
111 | |||
112 | skb_pull(skb, 2); /* Remove the length bytes */ | ||
113 | skb_trim(skb, len); /* Set the length of the data */ | ||
114 | |||
115 | if ((err = lapb_data_received(lapbeth->axdev, skb)) != LAPB_OK) { | ||
116 | printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n", err); | ||
117 | goto drop_unlock; | ||
118 | } | ||
119 | out: | ||
120 | rcu_read_unlock(); | ||
121 | return 0; | ||
122 | drop_unlock: | ||
123 | kfree_skb(skb); | ||
124 | goto out; | ||
125 | drop: | ||
126 | kfree_skb(skb); | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb) | ||
131 | { | ||
132 | unsigned char *ptr; | ||
133 | |||
134 | skb_push(skb, 1); | ||
135 | |||
136 | if (skb_cow(skb, 1)) | ||
137 | return NET_RX_DROP; | ||
138 | |||
139 | ptr = skb->data; | ||
140 | *ptr = 0x00; | ||
141 | |||
142 | skb->protocol = x25_type_trans(skb, dev); | ||
143 | skb->dev->last_rx = jiffies; | ||
144 | return netif_rx(skb); | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Send a LAPB frame via an ethernet interface | ||
149 | */ | ||
150 | static int lapbeth_xmit(struct sk_buff *skb, struct net_device *dev) | ||
151 | { | ||
152 | int err = -ENODEV; | ||
153 | |||
154 | /* | ||
155 | * Just to be *really* sure not to send anything if the interface | ||
156 | * is down, the ethernet device may have gone. | ||
157 | */ | ||
158 | if (!netif_running(dev)) { | ||
159 | goto drop; | ||
160 | } | ||
161 | |||
162 | switch (skb->data[0]) { | ||
163 | case 0x00: | ||
164 | err = 0; | ||
165 | break; | ||
166 | case 0x01: | ||
167 | if ((err = lapb_connect_request(dev)) != LAPB_OK) | ||
168 | printk(KERN_ERR "lapbeth: lapb_connect_request " | ||
169 | "error: %d\n", err); | ||
170 | goto drop_ok; | ||
171 | case 0x02: | ||
172 | if ((err = lapb_disconnect_request(dev)) != LAPB_OK) | ||
173 | printk(KERN_ERR "lapbeth: lapb_disconnect_request " | ||
174 | "err: %d\n", err); | ||
175 | /* Fall thru */ | ||
176 | default: | ||
177 | goto drop_ok; | ||
178 | } | ||
179 | |||
180 | skb_pull(skb, 1); | ||
181 | |||
182 | if ((err = lapb_data_request(dev, skb)) != LAPB_OK) { | ||
183 | printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err); | ||
184 | err = -ENOMEM; | ||
185 | goto drop; | ||
186 | } | ||
187 | err = 0; | ||
188 | out: | ||
189 | return err; | ||
190 | drop_ok: | ||
191 | err = 0; | ||
192 | drop: | ||
193 | kfree_skb(skb); | ||
194 | goto out; | ||
195 | } | ||
196 | |||
197 | static void lapbeth_data_transmit(struct net_device *ndev, struct sk_buff *skb) | ||
198 | { | ||
199 | struct lapbethdev *lapbeth = netdev_priv(ndev); | ||
200 | unsigned char *ptr; | ||
201 | struct net_device *dev; | ||
202 | int size = skb->len; | ||
203 | |||
204 | skb->protocol = htons(ETH_P_X25); | ||
205 | |||
206 | ptr = skb_push(skb, 2); | ||
207 | |||
208 | *ptr++ = size % 256; | ||
209 | *ptr++ = size / 256; | ||
210 | |||
211 | lapbeth->stats.tx_packets++; | ||
212 | lapbeth->stats.tx_bytes += size; | ||
213 | |||
214 | skb->dev = dev = lapbeth->ethdev; | ||
215 | |||
216 | dev->hard_header(skb, dev, ETH_P_DEC, bcast_addr, NULL, 0); | ||
217 | |||
218 | dev_queue_xmit(skb); | ||
219 | } | ||
220 | |||
221 | static void lapbeth_connected(struct net_device *dev, int reason) | ||
222 | { | ||
223 | unsigned char *ptr; | ||
224 | struct sk_buff *skb = dev_alloc_skb(1); | ||
225 | |||
226 | if (!skb) { | ||
227 | printk(KERN_ERR "lapbeth: out of memory\n"); | ||
228 | return; | ||
229 | } | ||
230 | |||
231 | ptr = skb_put(skb, 1); | ||
232 | *ptr = 0x01; | ||
233 | |||
234 | skb->protocol = x25_type_trans(skb, dev); | ||
235 | skb->dev->last_rx = jiffies; | ||
236 | netif_rx(skb); | ||
237 | } | ||
238 | |||
239 | static void lapbeth_disconnected(struct net_device *dev, int reason) | ||
240 | { | ||
241 | unsigned char *ptr; | ||
242 | struct sk_buff *skb = dev_alloc_skb(1); | ||
243 | |||
244 | if (!skb) { | ||
245 | printk(KERN_ERR "lapbeth: out of memory\n"); | ||
246 | return; | ||
247 | } | ||
248 | |||
249 | ptr = skb_put(skb, 1); | ||
250 | *ptr = 0x02; | ||
251 | |||
252 | skb->protocol = x25_type_trans(skb, dev); | ||
253 | skb->dev->last_rx = jiffies; | ||
254 | netif_rx(skb); | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Statistics | ||
259 | */ | ||
260 | static struct net_device_stats *lapbeth_get_stats(struct net_device *dev) | ||
261 | { | ||
262 | struct lapbethdev *lapbeth = netdev_priv(dev); | ||
263 | return &lapbeth->stats; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * Set AX.25 callsign | ||
268 | */ | ||
269 | static int lapbeth_set_mac_address(struct net_device *dev, void *addr) | ||
270 | { | ||
271 | struct sockaddr *sa = addr; | ||
272 | memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | |||
277 | static struct lapb_register_struct lapbeth_callbacks = { | ||
278 | .connect_confirmation = lapbeth_connected, | ||
279 | .connect_indication = lapbeth_connected, | ||
280 | .disconnect_confirmation = lapbeth_disconnected, | ||
281 | .disconnect_indication = lapbeth_disconnected, | ||
282 | .data_indication = lapbeth_data_indication, | ||
283 | .data_transmit = lapbeth_data_transmit, | ||
284 | |||
285 | }; | ||
286 | |||
287 | /* | ||
288 | * open/close a device | ||
289 | */ | ||
290 | static int lapbeth_open(struct net_device *dev) | ||
291 | { | ||
292 | int err; | ||
293 | |||
294 | if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) { | ||
295 | printk(KERN_ERR "lapbeth: lapb_register error - %d\n", err); | ||
296 | return -ENODEV; | ||
297 | } | ||
298 | |||
299 | netif_start_queue(dev); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int lapbeth_close(struct net_device *dev) | ||
304 | { | ||
305 | int err; | ||
306 | |||
307 | netif_stop_queue(dev); | ||
308 | |||
309 | if ((err = lapb_unregister(dev)) != LAPB_OK) | ||
310 | printk(KERN_ERR "lapbeth: lapb_unregister error - %d\n", err); | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | /* ------------------------------------------------------------------------ */ | ||
316 | |||
317 | static void lapbeth_setup(struct net_device *dev) | ||
318 | { | ||
319 | dev->hard_start_xmit = lapbeth_xmit; | ||
320 | dev->open = lapbeth_open; | ||
321 | dev->stop = lapbeth_close; | ||
322 | dev->destructor = free_netdev; | ||
323 | dev->set_mac_address = lapbeth_set_mac_address; | ||
324 | dev->get_stats = lapbeth_get_stats; | ||
325 | dev->type = ARPHRD_X25; | ||
326 | dev->hard_header_len = 3; | ||
327 | dev->mtu = 1000; | ||
328 | dev->addr_len = 0; | ||
329 | SET_MODULE_OWNER(dev); | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * Setup a new device. | ||
334 | */ | ||
335 | static int lapbeth_new_device(struct net_device *dev) | ||
336 | { | ||
337 | struct net_device *ndev; | ||
338 | struct lapbethdev *lapbeth; | ||
339 | int rc = -ENOMEM; | ||
340 | |||
341 | ASSERT_RTNL(); | ||
342 | |||
343 | ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", | ||
344 | lapbeth_setup); | ||
345 | if (!ndev) | ||
346 | goto out; | ||
347 | |||
348 | lapbeth = netdev_priv(ndev); | ||
349 | lapbeth->axdev = ndev; | ||
350 | |||
351 | dev_hold(dev); | ||
352 | lapbeth->ethdev = dev; | ||
353 | |||
354 | rc = dev_alloc_name(ndev, ndev->name); | ||
355 | if (rc < 0) | ||
356 | goto fail; | ||
357 | |||
358 | rc = -EIO; | ||
359 | if (register_netdevice(ndev)) | ||
360 | goto fail; | ||
361 | |||
362 | list_add_rcu(&lapbeth->node, &lapbeth_devices); | ||
363 | rc = 0; | ||
364 | out: | ||
365 | return rc; | ||
366 | fail: | ||
367 | dev_put(dev); | ||
368 | free_netdev(ndev); | ||
369 | kfree(lapbeth); | ||
370 | goto out; | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * Free a lapb network device. | ||
375 | */ | ||
376 | static void lapbeth_free_device(struct lapbethdev *lapbeth) | ||
377 | { | ||
378 | dev_put(lapbeth->ethdev); | ||
379 | list_del_rcu(&lapbeth->node); | ||
380 | unregister_netdevice(lapbeth->axdev); | ||
381 | } | ||
382 | |||
383 | /* | ||
384 | * Handle device status changes. | ||
385 | * | ||
386 | * Called from notifier with RTNL held. | ||
387 | */ | ||
388 | static int lapbeth_device_event(struct notifier_block *this, | ||
389 | unsigned long event, void *ptr) | ||
390 | { | ||
391 | struct lapbethdev *lapbeth; | ||
392 | struct net_device *dev = ptr; | ||
393 | |||
394 | if (!dev_is_ethdev(dev)) | ||
395 | return NOTIFY_DONE; | ||
396 | |||
397 | switch (event) { | ||
398 | case NETDEV_UP: | ||
399 | /* New ethernet device -> new LAPB interface */ | ||
400 | if (lapbeth_get_x25_dev(dev) == NULL) | ||
401 | lapbeth_new_device(dev); | ||
402 | break; | ||
403 | case NETDEV_DOWN: | ||
404 | /* ethernet device closed -> close LAPB interface */ | ||
405 | lapbeth = lapbeth_get_x25_dev(dev); | ||
406 | if (lapbeth) | ||
407 | dev_close(lapbeth->axdev); | ||
408 | break; | ||
409 | case NETDEV_UNREGISTER: | ||
410 | /* ethernet device disappears -> remove LAPB interface */ | ||
411 | lapbeth = lapbeth_get_x25_dev(dev); | ||
412 | if (lapbeth) | ||
413 | lapbeth_free_device(lapbeth); | ||
414 | break; | ||
415 | } | ||
416 | |||
417 | return NOTIFY_DONE; | ||
418 | } | ||
419 | |||
420 | /* ------------------------------------------------------------------------ */ | ||
421 | |||
422 | static struct packet_type lapbeth_packet_type = { | ||
423 | .type = __constant_htons(ETH_P_DEC), | ||
424 | .func = lapbeth_rcv, | ||
425 | }; | ||
426 | |||
427 | static struct notifier_block lapbeth_dev_notifier = { | ||
428 | .notifier_call = lapbeth_device_event, | ||
429 | }; | ||
430 | |||
431 | static char banner[] __initdata = KERN_INFO "LAPB Ethernet driver version 0.02\n"; | ||
432 | |||
433 | static int __init lapbeth_init_driver(void) | ||
434 | { | ||
435 | dev_add_pack(&lapbeth_packet_type); | ||
436 | |||
437 | register_netdevice_notifier(&lapbeth_dev_notifier); | ||
438 | |||
439 | printk(banner); | ||
440 | |||
441 | return 0; | ||
442 | } | ||
443 | module_init(lapbeth_init_driver); | ||
444 | |||
445 | static void __exit lapbeth_cleanup_driver(void) | ||
446 | { | ||
447 | struct lapbethdev *lapbeth; | ||
448 | struct list_head *entry, *tmp; | ||
449 | |||
450 | dev_remove_pack(&lapbeth_packet_type); | ||
451 | unregister_netdevice_notifier(&lapbeth_dev_notifier); | ||
452 | |||
453 | rtnl_lock(); | ||
454 | list_for_each_safe(entry, tmp, &lapbeth_devices) { | ||
455 | lapbeth = list_entry(entry, struct lapbethdev, node); | ||
456 | |||
457 | unregister_netdevice(lapbeth->axdev); | ||
458 | } | ||
459 | rtnl_unlock(); | ||
460 | } | ||
461 | module_exit(lapbeth_cleanup_driver); | ||
462 | |||
463 | MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>"); | ||
464 | MODULE_DESCRIPTION("The unofficial LAPB over Ethernet driver"); | ||
465 | MODULE_LICENSE("GPL"); | ||