ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / net / wan / hdlc_x25.c
1 /*
2  * Generic HDLC support routines for Linux
3  * X.25 support
4  *
5  * Copyright (C) 1999 - 2003 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 /* These functions are callbacks called by LAPB layer */
27
28 static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
29 {
30         struct sk_buff *skb;
31         unsigned char *ptr;
32
33         if ((skb = dev_alloc_skb(1)) == NULL) {
34                 printk(KERN_ERR "%s: out of memory\n", dev->name);
35                 return;
36         }
37
38         ptr = skb_put(skb, 1);
39         *ptr = code;
40
41         skb->dev = dev;
42         skb->protocol = htons(ETH_P_X25);
43         skb->mac.raw = skb->data;
44         skb->pkt_type = PACKET_HOST;
45
46         netif_rx(skb);
47 }
48
49
50
51 static void x25_connected(struct net_device *dev, int reason)
52 {
53         x25_connect_disconnect(dev, reason, 1);
54 }
55
56
57
58 static void x25_disconnected(struct net_device *dev, int reason)
59 {
60         x25_connect_disconnect(dev, reason, 2);
61 }
62
63
64
65 static 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->dev = dev;
78         skb->protocol = htons(ETH_P_X25);
79         skb->mac.raw = skb->data;
80         skb->pkt_type = PACKET_HOST;
81
82         return netif_rx(skb);
83 }
84
85
86
87 static void x25_data_transmit(struct net_device *dev, struct sk_buff *skb)
88 {
89         hdlc_device *hdlc = dev_to_hdlc(dev);
90         hdlc->xmit(skb, dev); /* Ignore return value :-( */
91 }
92
93
94
95 static int x25_xmit(struct sk_buff *skb, struct net_device *dev)
96 {
97         int result;
98
99
100         /* X.25 to LAPB */
101         switch (skb->data[0]) {
102         case 0:         /* Data to be transmitted */
103                 skb_pull(skb, 1);
104                 if ((result = lapb_data_request(dev, skb)) != LAPB_OK)
105                         dev_kfree_skb(skb);
106                 return 0;
107
108         case 1:
109                 if ((result = lapb_connect_request(dev))!= LAPB_OK) {
110                         if (result == LAPB_CONNECTED)
111                                 /* Send connect confirm. msg to level 3 */
112                                 x25_connected(dev, 0);
113                         else
114                                 printk(KERN_ERR "%s: LAPB connect request "
115                                        "failed, error code = %i\n",
116                                        dev->name, result);
117                 }
118                 break;
119
120         case 2:
121                 if ((result = lapb_disconnect_request(dev)) != LAPB_OK) {
122                         if (result == LAPB_NOTCONNECTED)
123                                 /* Send disconnect confirm. msg to level 3 */
124                                 x25_disconnected(dev, 0);
125                         else
126                                 printk(KERN_ERR "%s: LAPB disconnect request "
127                                        "failed, error code = %i\n",
128                                        dev->name, result);
129                 }
130                 break;
131
132         default:                /* to be defined */
133                 break;
134         }
135
136         dev_kfree_skb(skb);
137         return 0;
138 }
139
140
141
142 static int x25_open(struct net_device *dev)
143 {
144         struct lapb_register_struct cb;
145         int result;
146
147         cb.connect_confirmation = x25_connected;
148         cb.connect_indication = x25_connected;
149         cb.disconnect_confirmation = x25_disconnected;
150         cb.disconnect_indication = x25_disconnected;
151         cb.data_indication = x25_data_indication;
152         cb.data_transmit = x25_data_transmit;
153
154         result = lapb_register(dev, &cb);
155         if (result != LAPB_OK)
156                 return result;
157         return 0;
158 }
159
160
161
162 static void x25_close(struct net_device *dev)
163 {
164         lapb_unregister(dev);
165 }
166
167
168
169 static int x25_rx(struct sk_buff *skb)
170 {
171         hdlc_device *hdlc = dev_to_hdlc(skb->dev);
172
173         if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
174                 hdlc->stats.rx_dropped++;
175                 return NET_RX_DROP;
176         }
177
178         if (lapb_data_received(skb->dev, skb) == LAPB_OK)
179                 return NET_RX_SUCCESS;
180
181         hdlc->stats.rx_errors++;
182         dev_kfree_skb_any(skb);
183         return NET_RX_DROP;
184 }
185
186
187
188 int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
189 {
190         hdlc_device *hdlc = dev_to_hdlc(dev);
191         int result;
192
193         switch (ifr->ifr_settings.type) {
194         case IF_GET_PROTO:
195                 ifr->ifr_settings.type = IF_PROTO_X25;
196                 return 0; /* return protocol only, no settable parameters */
197
198         case IF_PROTO_X25:
199                 if(!capable(CAP_NET_ADMIN))
200                         return -EPERM;
201
202                 if(dev->flags & IFF_UP)
203                         return -EBUSY;
204
205                 result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
206                 if (result)
207                         return result;
208
209                 hdlc_proto_detach(hdlc);
210                 memset(&hdlc->proto, 0, sizeof(hdlc->proto));
211
212                 hdlc->proto.open = x25_open;
213                 hdlc->proto.close = x25_close;
214                 hdlc->proto.netif_rx = x25_rx;
215                 hdlc->proto.type_trans = NULL;
216                 hdlc->proto.id = IF_PROTO_X25;
217                 dev->hard_start_xmit = x25_xmit;
218                 dev->hard_header = NULL;
219                 dev->type = ARPHRD_X25;
220                 dev->addr_len = 0;
221                 return 0;
222         }
223
224         return -EINVAL;
225 }