This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / wan / comx-proto-ppp.c
1 /*
2  * Synchronous PPP / Cisco-HDLC driver for the COMX boards
3  *
4  * Author: Gergely Madarasz <gorgo@itc.hu>
5  *
6  * based on skeleton code by Tivadar Szemethy <tiv@itc.hu>
7  *
8  * Copyright (C) 1999 ITConsult-Pro Co. <info@itc.hu>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version
13  * 2 of the License, or (at your option) any later version.
14  *
15  *
16  * Version 0.10 (99/06/10):
17  *              - written the first code :)
18  *
19  * Version 0.20 (99/06/16):
20  *              - added hdlc protocol 
21  *              - protocol up is IFF_RUNNING
22  *
23  * Version 0.21 (99/07/15):
24  *              - some small fixes with the line status
25  *
26  * Version 0.22 (99/08/05):
27  *              - don't test IFF_RUNNING but the pp_link_state of the sppp
28  * 
29  * Version 0.23 (99/12/02):
30  *              - tbusy fixes
31  *
32  */
33
34 #define VERSION "0.23"
35
36 #include <linux/module.h>
37 #include <linux/types.h>
38 #include <linux/jiffies.h>
39 #include <linux/netdevice.h>
40 #include <linux/proc_fs.h>
41 #include <linux/if_arp.h>
42 #include <linux/inetdevice.h>
43 #include <asm/uaccess.h>
44 #include <linux/init.h>
45
46 #include <net/syncppp.h>
47 #include        "comx.h"
48
49 MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
50 MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
51 MODULE_LICENSE("GPL");
52
53 static struct comx_protocol syncppp_protocol;
54 static struct comx_protocol hdlc_protocol;
55
56 struct syncppp_data {
57         struct timer_list status_timer;
58 };
59
60 static void syncppp_status_timerfun(unsigned long d) {
61         struct net_device *dev=(struct net_device *)d;
62         struct comx_channel *ch=dev->priv;
63         struct syncppp_data *spch=ch->LINE_privdata;
64         struct sppp *sp = (struct sppp *)sppp_of(dev);
65         
66         if(!(ch->line_status & PROTO_UP) && 
67             (sp->pp_link_state==SPPP_LINK_UP)) {
68                 comx_status(dev, ch->line_status | PROTO_UP);
69         }
70         if((ch->line_status & PROTO_UP) &&
71             (sp->pp_link_state==SPPP_LINK_DOWN)) {
72                 comx_status(dev, ch->line_status & ~PROTO_UP);
73         }
74         mod_timer(&spch->status_timer,jiffies + HZ*3);
75 }
76
77 static int syncppp_tx(struct net_device *dev) 
78 {
79         struct comx_channel *ch=dev->priv;
80         
81         if(ch->line_status & LINE_UP) {
82                 netif_wake_queue(dev);
83         }
84         return 0;
85 }
86
87 static void syncppp_status(struct net_device *dev, unsigned short status)
88 {
89         status &= ~(PROTO_UP | PROTO_LOOP);
90         if(status & LINE_UP) {
91                 netif_wake_queue(dev);
92                 sppp_open(dev);
93         } else  {
94                 /* Line went down */
95                 netif_stop_queue(dev);
96                 sppp_close(dev);
97         }
98         comx_status(dev, status);
99 }
100
101 static int syncppp_open(struct net_device *dev)
102 {
103         struct comx_channel *ch = dev->priv;
104         struct syncppp_data *spch = ch->LINE_privdata;
105
106         if (!(ch->init_status & HW_OPEN)) return -ENODEV;
107
108         ch->init_status |= LINE_OPEN;
109         ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
110
111         if(ch->line_status & LINE_UP) {
112                 sppp_open(dev);
113         }
114
115         init_timer(&spch->status_timer);
116         spch->status_timer.function=syncppp_status_timerfun;
117         spch->status_timer.data=(unsigned long)dev;
118         spch->status_timer.expires=jiffies + HZ*3;
119         add_timer(&spch->status_timer);
120         
121         return 0;
122 }
123
124 static int syncppp_close(struct net_device *dev)
125 {
126         struct comx_channel *ch = dev->priv;
127         struct syncppp_data *spch = ch->LINE_privdata;
128
129         if (!(ch->init_status & HW_OPEN)) return -ENODEV;
130         del_timer(&spch->status_timer);
131         
132         sppp_close(dev);
133
134         ch->init_status &= ~LINE_OPEN;
135         ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
136
137         return 0;
138 }
139
140 static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
141 {
142         struct comx_channel *ch = dev->priv;
143
144         netif_stop_queue(dev);
145         switch(ch->HW_send_packet(dev, skb)) {
146                 case FRAME_QUEUED:
147                         netif_wake_queue(dev);
148                         break;
149                 case FRAME_ACCEPTED:
150                 case FRAME_DROPPED:
151                         break;
152                 case FRAME_ERROR:
153                         printk(KERN_ERR "%s: Transmit frame error (len %d)\n", 
154                                 dev->name, skb->len);
155                 break;
156         }
157         return 0;
158 }
159
160
161 static int syncppp_statistics(struct net_device *dev, char *page) 
162 {
163         int len = 0;
164
165         len += sprintf(page + len, " ");
166         return len;
167 }
168
169
170 static int syncppp_exit(struct net_device *dev) 
171 {
172         struct comx_channel *ch = dev->priv;
173
174         sppp_detach(dev);
175
176         dev->flags = 0;
177         dev->type = 0;
178         dev->mtu = 0;
179
180         ch->LINE_rx = NULL;
181         ch->LINE_tx = NULL;
182         ch->LINE_status = NULL;
183         ch->LINE_open = NULL;
184         ch->LINE_close = NULL;
185         ch->LINE_xmit = NULL;
186         ch->LINE_header = NULL;
187         ch->LINE_rebuild_header = NULL;
188         ch->LINE_statistics = NULL;
189
190         kfree(ch->LINE_privdata);
191         ch->LINE_privdata = NULL;
192
193         MOD_DEC_USE_COUNT;
194         return 0;
195 }
196
197 static int syncppp_init(struct net_device *dev)
198 {
199         struct comx_channel *ch = dev->priv;
200         struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
201
202         ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
203         if (!ch->LINE_privdata)
204                 return -ENOMEM;
205
206         pppdev->dev = dev;
207         sppp_attach(pppdev);
208
209         if(ch->protocol == &hdlc_protocol) {
210                 pppdev->sppp.pp_flags |= PP_CISCO;
211                 dev->type = ARPHRD_HDLC;
212         } else {
213                 pppdev->sppp.pp_flags &= ~PP_CISCO;
214                 dev->type = ARPHRD_PPP;
215         }
216
217         ch->LINE_rx = sppp_input;
218         ch->LINE_tx = syncppp_tx;
219         ch->LINE_status = syncppp_status;
220         ch->LINE_open = syncppp_open;
221         ch->LINE_close = syncppp_close;
222         ch->LINE_xmit = syncppp_xmit;
223         ch->LINE_header = NULL;
224         ch->LINE_statistics = syncppp_statistics;
225
226
227         MOD_INC_USE_COUNT;
228         return 0;
229 }
230
231 static struct comx_protocol syncppp_protocol = {
232         "ppp", 
233         VERSION,
234         ARPHRD_PPP, 
235         syncppp_init, 
236         syncppp_exit, 
237         NULL 
238 };
239
240 static struct comx_protocol hdlc_protocol = {
241         "hdlc", 
242         VERSION,
243         ARPHRD_PPP, 
244         syncppp_init, 
245         syncppp_exit, 
246         NULL 
247 };
248
249 static int __init comx_proto_ppp_init(void)
250 {
251         int ret;
252
253         ret = comx_register_protocol(&hdlc_protocol);
254         if (!ret) {
255                 ret = comx_register_protocol(&syncppp_protocol);
256                 if (ret)
257                         comx_unregister_protocol(hdlc_protocol.name);
258         }
259         return ret;
260 }
261
262 static void __exit comx_proto_ppp_exit(void)
263 {
264         comx_unregister_protocol(syncppp_protocol.name);
265         comx_unregister_protocol(hdlc_protocol.name);
266 }
267
268 module_init(comx_proto_ppp_init);
269 module_exit(comx_proto_ppp_exit);