1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * "LAPB via ethernet" driver release 001
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * This code REQUIRES 2.1.15 or higher/ NET3.038
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * This is a "pseudo" network driver to allow LAPB over Ethernet.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * This driver can use any ethernet destination address, and can be
10*4882a593Smuzhiyun * limited to accept frames from one dedicated ethernet card only.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * History
13*4882a593Smuzhiyun * LAPBETH 001 Jonathan Naylor Cloned from bpqether.c
14*4882a593Smuzhiyun * 2000-10-29 Henner Eisen lapb_data_indication() return status.
15*4882a593Smuzhiyun * 2000-11-14 Henner Eisen dev_hold/put, NETDEV_GOING_DOWN support
16*4882a593Smuzhiyun */
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #include <linux/errno.h>
21*4882a593Smuzhiyun #include <linux/types.h>
22*4882a593Smuzhiyun #include <linux/socket.h>
23*4882a593Smuzhiyun #include <linux/in.h>
24*4882a593Smuzhiyun #include <linux/slab.h>
25*4882a593Smuzhiyun #include <linux/kernel.h>
26*4882a593Smuzhiyun #include <linux/string.h>
27*4882a593Smuzhiyun #include <linux/net.h>
28*4882a593Smuzhiyun #include <linux/inet.h>
29*4882a593Smuzhiyun #include <linux/netdevice.h>
30*4882a593Smuzhiyun #include <linux/if_arp.h>
31*4882a593Smuzhiyun #include <linux/skbuff.h>
32*4882a593Smuzhiyun #include <net/sock.h>
33*4882a593Smuzhiyun #include <linux/uaccess.h>
34*4882a593Smuzhiyun #include <linux/mm.h>
35*4882a593Smuzhiyun #include <linux/interrupt.h>
36*4882a593Smuzhiyun #include <linux/notifier.h>
37*4882a593Smuzhiyun #include <linux/stat.h>
38*4882a593Smuzhiyun #include <linux/module.h>
39*4882a593Smuzhiyun #include <linux/lapb.h>
40*4882a593Smuzhiyun #include <linux/init.h>
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun #include <net/x25device.h>
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun static const u8 bcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* If this number is made larger, check that the temporary string buffer
47*4882a593Smuzhiyun * in lapbeth_new_device is large enough to store the probe device name.*/
48*4882a593Smuzhiyun #define MAXLAPBDEV 100
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun struct lapbethdev {
51*4882a593Smuzhiyun struct list_head node;
52*4882a593Smuzhiyun struct net_device *ethdev; /* link to ethernet device */
53*4882a593Smuzhiyun struct net_device *axdev; /* lapbeth device (lapb#) */
54*4882a593Smuzhiyun bool up;
55*4882a593Smuzhiyun spinlock_t up_lock; /* Protects "up" */
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun static LIST_HEAD(lapbeth_devices);
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun /* ------------------------------------------------------------------------ */
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /*
63*4882a593Smuzhiyun * Get the LAPB device for the ethernet device
64*4882a593Smuzhiyun */
lapbeth_get_x25_dev(struct net_device * dev)65*4882a593Smuzhiyun static struct lapbethdev *lapbeth_get_x25_dev(struct net_device *dev)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun struct lapbethdev *lapbeth;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun list_for_each_entry_rcu(lapbeth, &lapbeth_devices, node, lockdep_rtnl_is_held()) {
70*4882a593Smuzhiyun if (lapbeth->ethdev == dev)
71*4882a593Smuzhiyun return lapbeth;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun return NULL;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
dev_is_ethdev(struct net_device * dev)76*4882a593Smuzhiyun static __inline__ int dev_is_ethdev(struct net_device *dev)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun return dev->type == ARPHRD_ETHER && strncmp(dev->name, "dummy", 5);
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun /* ------------------------------------------------------------------------ */
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /*
84*4882a593Smuzhiyun * Receive a LAPB frame via an ethernet interface.
85*4882a593Smuzhiyun */
lapbeth_rcv(struct sk_buff * skb,struct net_device * dev,struct packet_type * ptype,struct net_device * orig_dev)86*4882a593Smuzhiyun static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun int len, err;
89*4882a593Smuzhiyun struct lapbethdev *lapbeth;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun if (dev_net(dev) != &init_net)
92*4882a593Smuzhiyun goto drop;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
95*4882a593Smuzhiyun return NET_RX_DROP;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun if (!pskb_may_pull(skb, 2))
98*4882a593Smuzhiyun goto drop;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun rcu_read_lock();
101*4882a593Smuzhiyun lapbeth = lapbeth_get_x25_dev(dev);
102*4882a593Smuzhiyun if (!lapbeth)
103*4882a593Smuzhiyun goto drop_unlock_rcu;
104*4882a593Smuzhiyun spin_lock_bh(&lapbeth->up_lock);
105*4882a593Smuzhiyun if (!lapbeth->up)
106*4882a593Smuzhiyun goto drop_unlock;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun len = skb->data[0] + skb->data[1] * 256;
109*4882a593Smuzhiyun dev->stats.rx_packets++;
110*4882a593Smuzhiyun dev->stats.rx_bytes += len;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun skb_pull(skb, 2); /* Remove the length bytes */
113*4882a593Smuzhiyun skb_trim(skb, len); /* Set the length of the data */
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun if ((err = lapb_data_received(lapbeth->axdev, skb)) != LAPB_OK) {
116*4882a593Smuzhiyun printk(KERN_DEBUG "lapbether: lapb_data_received err - %d\n", err);
117*4882a593Smuzhiyun goto drop_unlock;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun out:
120*4882a593Smuzhiyun spin_unlock_bh(&lapbeth->up_lock);
121*4882a593Smuzhiyun rcu_read_unlock();
122*4882a593Smuzhiyun return 0;
123*4882a593Smuzhiyun drop_unlock:
124*4882a593Smuzhiyun kfree_skb(skb);
125*4882a593Smuzhiyun goto out;
126*4882a593Smuzhiyun drop_unlock_rcu:
127*4882a593Smuzhiyun rcu_read_unlock();
128*4882a593Smuzhiyun drop:
129*4882a593Smuzhiyun kfree_skb(skb);
130*4882a593Smuzhiyun return 0;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
lapbeth_data_indication(struct net_device * dev,struct sk_buff * skb)133*4882a593Smuzhiyun static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun unsigned char *ptr;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun if (skb_cow(skb, 1)) {
138*4882a593Smuzhiyun kfree_skb(skb);
139*4882a593Smuzhiyun return NET_RX_DROP;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun skb_push(skb, 1);
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun ptr = skb->data;
145*4882a593Smuzhiyun *ptr = X25_IFACE_DATA;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun skb->protocol = x25_type_trans(skb, dev);
148*4882a593Smuzhiyun return netif_rx(skb);
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun /*
152*4882a593Smuzhiyun * Send a LAPB frame via an ethernet interface
153*4882a593Smuzhiyun */
lapbeth_xmit(struct sk_buff * skb,struct net_device * dev)154*4882a593Smuzhiyun static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
155*4882a593Smuzhiyun struct net_device *dev)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun struct lapbethdev *lapbeth = netdev_priv(dev);
158*4882a593Smuzhiyun int err;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun spin_lock_bh(&lapbeth->up_lock);
161*4882a593Smuzhiyun if (!lapbeth->up)
162*4882a593Smuzhiyun goto drop;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun /* There should be a pseudo header of 1 byte added by upper layers.
165*4882a593Smuzhiyun * Check to make sure it is there before reading it.
166*4882a593Smuzhiyun */
167*4882a593Smuzhiyun if (skb->len < 1)
168*4882a593Smuzhiyun goto drop;
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun switch (skb->data[0]) {
171*4882a593Smuzhiyun case X25_IFACE_DATA:
172*4882a593Smuzhiyun break;
173*4882a593Smuzhiyun case X25_IFACE_CONNECT:
174*4882a593Smuzhiyun if ((err = lapb_connect_request(dev)) != LAPB_OK)
175*4882a593Smuzhiyun pr_err("lapb_connect_request error: %d\n", err);
176*4882a593Smuzhiyun goto drop;
177*4882a593Smuzhiyun case X25_IFACE_DISCONNECT:
178*4882a593Smuzhiyun if ((err = lapb_disconnect_request(dev)) != LAPB_OK)
179*4882a593Smuzhiyun pr_err("lapb_disconnect_request err: %d\n", err);
180*4882a593Smuzhiyun fallthrough;
181*4882a593Smuzhiyun default:
182*4882a593Smuzhiyun goto drop;
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun skb_pull(skb, 1);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun if ((err = lapb_data_request(dev, skb)) != LAPB_OK) {
188*4882a593Smuzhiyun pr_err("lapb_data_request error - %d\n", err);
189*4882a593Smuzhiyun goto drop;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun out:
192*4882a593Smuzhiyun spin_unlock_bh(&lapbeth->up_lock);
193*4882a593Smuzhiyun return NETDEV_TX_OK;
194*4882a593Smuzhiyun drop:
195*4882a593Smuzhiyun kfree_skb(skb);
196*4882a593Smuzhiyun goto out;
197*4882a593Smuzhiyun }
198*4882a593Smuzhiyun
lapbeth_data_transmit(struct net_device * ndev,struct sk_buff * skb)199*4882a593Smuzhiyun static void lapbeth_data_transmit(struct net_device *ndev, struct sk_buff *skb)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun struct lapbethdev *lapbeth = netdev_priv(ndev);
202*4882a593Smuzhiyun unsigned char *ptr;
203*4882a593Smuzhiyun struct net_device *dev;
204*4882a593Smuzhiyun int size = skb->len;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun ptr = skb_push(skb, 2);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun *ptr++ = size % 256;
209*4882a593Smuzhiyun *ptr++ = size / 256;
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun ndev->stats.tx_packets++;
212*4882a593Smuzhiyun ndev->stats.tx_bytes += size;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun skb->dev = dev = lapbeth->ethdev;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun skb->protocol = htons(ETH_P_DEC);
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun skb_reset_network_header(skb);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun dev_hard_header(skb, dev, ETH_P_DEC, bcast_addr, NULL, 0);
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun dev_queue_xmit(skb);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
lapbeth_connected(struct net_device * dev,int reason)225*4882a593Smuzhiyun static void lapbeth_connected(struct net_device *dev, int reason)
226*4882a593Smuzhiyun {
227*4882a593Smuzhiyun unsigned char *ptr;
228*4882a593Smuzhiyun struct sk_buff *skb = dev_alloc_skb(1);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun if (!skb) {
231*4882a593Smuzhiyun pr_err("out of memory\n");
232*4882a593Smuzhiyun return;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun ptr = skb_put(skb, 1);
236*4882a593Smuzhiyun *ptr = X25_IFACE_CONNECT;
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun skb->protocol = x25_type_trans(skb, dev);
239*4882a593Smuzhiyun netif_rx(skb);
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun
lapbeth_disconnected(struct net_device * dev,int reason)242*4882a593Smuzhiyun static void lapbeth_disconnected(struct net_device *dev, int reason)
243*4882a593Smuzhiyun {
244*4882a593Smuzhiyun unsigned char *ptr;
245*4882a593Smuzhiyun struct sk_buff *skb = dev_alloc_skb(1);
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun if (!skb) {
248*4882a593Smuzhiyun pr_err("out of memory\n");
249*4882a593Smuzhiyun return;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun ptr = skb_put(skb, 1);
253*4882a593Smuzhiyun *ptr = X25_IFACE_DISCONNECT;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun skb->protocol = x25_type_trans(skb, dev);
256*4882a593Smuzhiyun netif_rx(skb);
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun /*
260*4882a593Smuzhiyun * Set AX.25 callsign
261*4882a593Smuzhiyun */
lapbeth_set_mac_address(struct net_device * dev,void * addr)262*4882a593Smuzhiyun static int lapbeth_set_mac_address(struct net_device *dev, void *addr)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun struct sockaddr *sa = addr;
265*4882a593Smuzhiyun memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
266*4882a593Smuzhiyun return 0;
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun static const struct lapb_register_struct lapbeth_callbacks = {
271*4882a593Smuzhiyun .connect_confirmation = lapbeth_connected,
272*4882a593Smuzhiyun .connect_indication = lapbeth_connected,
273*4882a593Smuzhiyun .disconnect_confirmation = lapbeth_disconnected,
274*4882a593Smuzhiyun .disconnect_indication = lapbeth_disconnected,
275*4882a593Smuzhiyun .data_indication = lapbeth_data_indication,
276*4882a593Smuzhiyun .data_transmit = lapbeth_data_transmit,
277*4882a593Smuzhiyun };
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun /*
280*4882a593Smuzhiyun * open/close a device
281*4882a593Smuzhiyun */
lapbeth_open(struct net_device * dev)282*4882a593Smuzhiyun static int lapbeth_open(struct net_device *dev)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun struct lapbethdev *lapbeth = netdev_priv(dev);
285*4882a593Smuzhiyun int err;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {
288*4882a593Smuzhiyun pr_err("lapb_register error: %d\n", err);
289*4882a593Smuzhiyun return -ENODEV;
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun spin_lock_bh(&lapbeth->up_lock);
293*4882a593Smuzhiyun lapbeth->up = true;
294*4882a593Smuzhiyun spin_unlock_bh(&lapbeth->up_lock);
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun return 0;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
lapbeth_close(struct net_device * dev)299*4882a593Smuzhiyun static int lapbeth_close(struct net_device *dev)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun struct lapbethdev *lapbeth = netdev_priv(dev);
302*4882a593Smuzhiyun int err;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun spin_lock_bh(&lapbeth->up_lock);
305*4882a593Smuzhiyun lapbeth->up = false;
306*4882a593Smuzhiyun spin_unlock_bh(&lapbeth->up_lock);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun if ((err = lapb_unregister(dev)) != LAPB_OK)
309*4882a593Smuzhiyun pr_err("lapb_unregister error: %d\n", err);
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun return 0;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun /* ------------------------------------------------------------------------ */
315*4882a593Smuzhiyun
316*4882a593Smuzhiyun static const struct net_device_ops lapbeth_netdev_ops = {
317*4882a593Smuzhiyun .ndo_open = lapbeth_open,
318*4882a593Smuzhiyun .ndo_stop = lapbeth_close,
319*4882a593Smuzhiyun .ndo_start_xmit = lapbeth_xmit,
320*4882a593Smuzhiyun .ndo_set_mac_address = lapbeth_set_mac_address,
321*4882a593Smuzhiyun };
322*4882a593Smuzhiyun
lapbeth_setup(struct net_device * dev)323*4882a593Smuzhiyun static void lapbeth_setup(struct net_device *dev)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun dev->netdev_ops = &lapbeth_netdev_ops;
326*4882a593Smuzhiyun dev->needs_free_netdev = true;
327*4882a593Smuzhiyun dev->type = ARPHRD_X25;
328*4882a593Smuzhiyun dev->hard_header_len = 0;
329*4882a593Smuzhiyun dev->mtu = 1000;
330*4882a593Smuzhiyun dev->addr_len = 0;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun /*
334*4882a593Smuzhiyun * Setup a new device.
335*4882a593Smuzhiyun */
lapbeth_new_device(struct net_device * dev)336*4882a593Smuzhiyun static int lapbeth_new_device(struct net_device *dev)
337*4882a593Smuzhiyun {
338*4882a593Smuzhiyun struct net_device *ndev;
339*4882a593Smuzhiyun struct lapbethdev *lapbeth;
340*4882a593Smuzhiyun int rc = -ENOMEM;
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun ASSERT_RTNL();
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun ndev = alloc_netdev(sizeof(*lapbeth), "lapb%d", NET_NAME_UNKNOWN,
345*4882a593Smuzhiyun lapbeth_setup);
346*4882a593Smuzhiyun if (!ndev)
347*4882a593Smuzhiyun goto out;
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun /* When transmitting data:
350*4882a593Smuzhiyun * first this driver removes a pseudo header of 1 byte,
351*4882a593Smuzhiyun * then the lapb module prepends an LAPB header of at most 3 bytes,
352*4882a593Smuzhiyun * then this driver prepends a length field of 2 bytes,
353*4882a593Smuzhiyun * then the underlying Ethernet device prepends its own header.
354*4882a593Smuzhiyun */
355*4882a593Smuzhiyun ndev->needed_headroom = -1 + 3 + 2 + dev->hard_header_len
356*4882a593Smuzhiyun + dev->needed_headroom;
357*4882a593Smuzhiyun ndev->needed_tailroom = dev->needed_tailroom;
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun lapbeth = netdev_priv(ndev);
360*4882a593Smuzhiyun lapbeth->axdev = ndev;
361*4882a593Smuzhiyun
362*4882a593Smuzhiyun dev_hold(dev);
363*4882a593Smuzhiyun lapbeth->ethdev = dev;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun lapbeth->up = false;
366*4882a593Smuzhiyun spin_lock_init(&lapbeth->up_lock);
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun rc = -EIO;
369*4882a593Smuzhiyun if (register_netdevice(ndev))
370*4882a593Smuzhiyun goto fail;
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun list_add_rcu(&lapbeth->node, &lapbeth_devices);
373*4882a593Smuzhiyun rc = 0;
374*4882a593Smuzhiyun out:
375*4882a593Smuzhiyun return rc;
376*4882a593Smuzhiyun fail:
377*4882a593Smuzhiyun dev_put(dev);
378*4882a593Smuzhiyun free_netdev(ndev);
379*4882a593Smuzhiyun goto out;
380*4882a593Smuzhiyun }
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun /*
383*4882a593Smuzhiyun * Free a lapb network device.
384*4882a593Smuzhiyun */
lapbeth_free_device(struct lapbethdev * lapbeth)385*4882a593Smuzhiyun static void lapbeth_free_device(struct lapbethdev *lapbeth)
386*4882a593Smuzhiyun {
387*4882a593Smuzhiyun dev_put(lapbeth->ethdev);
388*4882a593Smuzhiyun list_del_rcu(&lapbeth->node);
389*4882a593Smuzhiyun unregister_netdevice(lapbeth->axdev);
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun /*
393*4882a593Smuzhiyun * Handle device status changes.
394*4882a593Smuzhiyun *
395*4882a593Smuzhiyun * Called from notifier with RTNL held.
396*4882a593Smuzhiyun */
lapbeth_device_event(struct notifier_block * this,unsigned long event,void * ptr)397*4882a593Smuzhiyun static int lapbeth_device_event(struct notifier_block *this,
398*4882a593Smuzhiyun unsigned long event, void *ptr)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun struct lapbethdev *lapbeth;
401*4882a593Smuzhiyun struct net_device *dev = netdev_notifier_info_to_dev(ptr);
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun if (dev_net(dev) != &init_net)
404*4882a593Smuzhiyun return NOTIFY_DONE;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun if (!dev_is_ethdev(dev) && !lapbeth_get_x25_dev(dev))
407*4882a593Smuzhiyun return NOTIFY_DONE;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun switch (event) {
410*4882a593Smuzhiyun case NETDEV_UP:
411*4882a593Smuzhiyun /* New ethernet device -> new LAPB interface */
412*4882a593Smuzhiyun if (lapbeth_get_x25_dev(dev) == NULL)
413*4882a593Smuzhiyun lapbeth_new_device(dev);
414*4882a593Smuzhiyun break;
415*4882a593Smuzhiyun case NETDEV_DOWN:
416*4882a593Smuzhiyun /* ethernet device closed -> close LAPB interface */
417*4882a593Smuzhiyun lapbeth = lapbeth_get_x25_dev(dev);
418*4882a593Smuzhiyun if (lapbeth)
419*4882a593Smuzhiyun dev_close(lapbeth->axdev);
420*4882a593Smuzhiyun break;
421*4882a593Smuzhiyun case NETDEV_UNREGISTER:
422*4882a593Smuzhiyun /* ethernet device disappears -> remove LAPB interface */
423*4882a593Smuzhiyun lapbeth = lapbeth_get_x25_dev(dev);
424*4882a593Smuzhiyun if (lapbeth)
425*4882a593Smuzhiyun lapbeth_free_device(lapbeth);
426*4882a593Smuzhiyun break;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun return NOTIFY_DONE;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /* ------------------------------------------------------------------------ */
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun static struct packet_type lapbeth_packet_type __read_mostly = {
435*4882a593Smuzhiyun .type = cpu_to_be16(ETH_P_DEC),
436*4882a593Smuzhiyun .func = lapbeth_rcv,
437*4882a593Smuzhiyun };
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun static struct notifier_block lapbeth_dev_notifier = {
440*4882a593Smuzhiyun .notifier_call = lapbeth_device_event,
441*4882a593Smuzhiyun };
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun static const char banner[] __initconst =
444*4882a593Smuzhiyun KERN_INFO "LAPB Ethernet driver version 0.02\n";
445*4882a593Smuzhiyun
lapbeth_init_driver(void)446*4882a593Smuzhiyun static int __init lapbeth_init_driver(void)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun dev_add_pack(&lapbeth_packet_type);
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun register_netdevice_notifier(&lapbeth_dev_notifier);
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun printk(banner);
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun return 0;
455*4882a593Smuzhiyun }
456*4882a593Smuzhiyun module_init(lapbeth_init_driver);
457*4882a593Smuzhiyun
lapbeth_cleanup_driver(void)458*4882a593Smuzhiyun static void __exit lapbeth_cleanup_driver(void)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun struct lapbethdev *lapbeth;
461*4882a593Smuzhiyun struct list_head *entry, *tmp;
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun dev_remove_pack(&lapbeth_packet_type);
464*4882a593Smuzhiyun unregister_netdevice_notifier(&lapbeth_dev_notifier);
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun rtnl_lock();
467*4882a593Smuzhiyun list_for_each_safe(entry, tmp, &lapbeth_devices) {
468*4882a593Smuzhiyun lapbeth = list_entry(entry, struct lapbethdev, node);
469*4882a593Smuzhiyun
470*4882a593Smuzhiyun dev_put(lapbeth->ethdev);
471*4882a593Smuzhiyun unregister_netdevice(lapbeth->axdev);
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun rtnl_unlock();
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun module_exit(lapbeth_cleanup_driver);
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
478*4882a593Smuzhiyun MODULE_DESCRIPTION("The unofficial LAPB over Ethernet driver");
479*4882a593Smuzhiyun MODULE_LICENSE("GPL");
480