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/version.h>
38 #include <linux/types.h>
39 #include <linux/sched.h>
40 #include <linux/netdevice.h>
41 #include <linux/proc_fs.h>
42 #include <linux/if_arp.h>
43 #include <linux/inetdevice.h>
44 #include <asm/uaccess.h>
45 #include <linux/init.h>
46 
47 #include <net/syncppp.h>
48 #include	"comx.h"
49 
50 MODULE_AUTHOR("Author: Gergely Madarasz <gorgo@itc.hu>");
51 MODULE_DESCRIPTION("Cisco-HDLC / Synchronous PPP driver for the COMX sync serial boards");
52 MODULE_LICENSE("GPL");
53 
54 static struct comx_protocol syncppp_protocol;
55 static struct comx_protocol hdlc_protocol;
56 
57 struct syncppp_data {
58 	struct timer_list status_timer;
59 };
60 
syncppp_status_timerfun(unsigned long d)61 static void syncppp_status_timerfun(unsigned long d) {
62 	struct net_device *dev=(struct net_device *)d;
63 	struct comx_channel *ch=dev->priv;
64 	struct syncppp_data *spch=ch->LINE_privdata;
65 	struct sppp *sp = (struct sppp *)sppp_of(dev);
66 
67 	if(!(ch->line_status & PROTO_UP) &&
68 	    (sp->pp_link_state==SPPP_LINK_UP)) {
69     		comx_status(dev, ch->line_status | PROTO_UP);
70 	}
71 	if((ch->line_status & PROTO_UP) &&
72 	    (sp->pp_link_state==SPPP_LINK_DOWN)) {
73 	    	comx_status(dev, ch->line_status & ~PROTO_UP);
74 	}
75 	mod_timer(&spch->status_timer,jiffies + HZ*3);
76 }
77 
syncppp_tx(struct net_device * dev)78 static int syncppp_tx(struct net_device *dev)
79 {
80 	struct comx_channel *ch=dev->priv;
81 
82 	if(ch->line_status & LINE_UP) {
83 		netif_wake_queue(dev);
84 	}
85 	return 0;
86 }
87 
syncppp_status(struct net_device * dev,unsigned short status)88 static void syncppp_status(struct net_device *dev, unsigned short status)
89 {
90 	status &= ~(PROTO_UP | PROTO_LOOP);
91 	if(status & LINE_UP) {
92 		netif_wake_queue(dev);
93 		sppp_open(dev);
94 	} else 	{
95 		/* Line went down */
96 		netif_stop_queue(dev);
97 		sppp_close(dev);
98 	}
99 	comx_status(dev, status);
100 }
101 
syncppp_open(struct net_device * dev)102 static int syncppp_open(struct net_device *dev)
103 {
104 	struct comx_channel *ch = dev->priv;
105 	struct syncppp_data *spch = ch->LINE_privdata;
106 
107 	if (!(ch->init_status & HW_OPEN)) return -ENODEV;
108 
109 	ch->init_status |= LINE_OPEN;
110 	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
111 
112 	if(ch->line_status & LINE_UP) {
113 		sppp_open(dev);
114 	}
115 
116 	init_timer(&spch->status_timer);
117 	spch->status_timer.function=syncppp_status_timerfun;
118 	spch->status_timer.data=(unsigned long)dev;
119 	spch->status_timer.expires=jiffies + HZ*3;
120 	add_timer(&spch->status_timer);
121 
122 	return 0;
123 }
124 
syncppp_close(struct net_device * dev)125 static int syncppp_close(struct net_device *dev)
126 {
127 	struct comx_channel *ch = dev->priv;
128 	struct syncppp_data *spch = ch->LINE_privdata;
129 
130 	if (!(ch->init_status & HW_OPEN)) return -ENODEV;
131 	del_timer(&spch->status_timer);
132 
133 	sppp_close(dev);
134 
135 	ch->init_status &= ~LINE_OPEN;
136 	ch->line_status &= ~(PROTO_UP | PROTO_LOOP);
137 
138 	return 0;
139 }
140 
syncppp_xmit(struct sk_buff * skb,struct net_device * dev)141 static int syncppp_xmit(struct sk_buff *skb, struct net_device *dev)
142 {
143 	struct comx_channel *ch = dev->priv;
144 
145 	netif_stop_queue(dev);
146 	switch(ch->HW_send_packet(dev, skb)) {
147 		case FRAME_QUEUED:
148 			netif_wake_queue(dev);
149 			break;
150 		case FRAME_ACCEPTED:
151 		case FRAME_DROPPED:
152 			break;
153 		case FRAME_ERROR:
154 			printk(KERN_ERR "%s: Transmit frame error (len %d)\n",
155 				dev->name, skb->len);
156 		break;
157 	}
158 	return 0;
159 }
160 
161 
syncppp_statistics(struct net_device * dev,char * page)162 static int syncppp_statistics(struct net_device *dev, char *page)
163 {
164 	int len = 0;
165 
166 	len += sprintf(page + len, " ");
167 	return len;
168 }
169 
170 
syncppp_exit(struct net_device * dev)171 static int syncppp_exit(struct net_device *dev)
172 {
173 	struct comx_channel *ch = dev->priv;
174 
175 	sppp_detach(dev);
176 
177 	dev->flags = 0;
178 	dev->type = 0;
179 	dev->mtu = 0;
180 
181 	ch->LINE_rx = NULL;
182 	ch->LINE_tx = NULL;
183 	ch->LINE_status = NULL;
184 	ch->LINE_open = NULL;
185 	ch->LINE_close = NULL;
186 	ch->LINE_xmit = NULL;
187 	ch->LINE_header	= NULL;
188 	ch->LINE_rebuild_header	= NULL;
189 	ch->LINE_statistics = NULL;
190 
191 	kfree(ch->LINE_privdata);
192 	ch->LINE_privdata = NULL;
193 
194 	MOD_DEC_USE_COUNT;
195 	return 0;
196 }
197 
syncppp_init(struct net_device * dev)198 static int syncppp_init(struct net_device *dev)
199 {
200 	struct comx_channel *ch = dev->priv;
201 	struct ppp_device *pppdev = (struct ppp_device *)ch->if_ptr;
202 
203 	ch->LINE_privdata = kmalloc(sizeof(struct syncppp_data), GFP_KERNEL);
204 	if (!ch->LINE_privdata)
205 		return -ENOMEM;
206 
207 	pppdev->dev = dev;
208 	sppp_attach(pppdev);
209 
210 	if(ch->protocol == &hdlc_protocol) {
211 		pppdev->sppp.pp_flags |= PP_CISCO;
212 		dev->type = ARPHRD_HDLC;
213 	} else {
214 		pppdev->sppp.pp_flags &= ~PP_CISCO;
215 		dev->type = ARPHRD_PPP;
216 	}
217 
218 	ch->LINE_rx = sppp_input;
219 	ch->LINE_tx = syncppp_tx;
220 	ch->LINE_status = syncppp_status;
221 	ch->LINE_open = syncppp_open;
222 	ch->LINE_close = syncppp_close;
223 	ch->LINE_xmit = syncppp_xmit;
224 	ch->LINE_header	= NULL;
225 	ch->LINE_statistics = syncppp_statistics;
226 
227 
228 	MOD_INC_USE_COUNT;
229 	return 0;
230 }
231 
232 static struct comx_protocol syncppp_protocol = {
233 	"ppp",
234 	VERSION,
235 	ARPHRD_PPP,
236 	syncppp_init,
237 	syncppp_exit,
238 	NULL
239 };
240 
241 static struct comx_protocol hdlc_protocol = {
242 	"hdlc",
243 	VERSION,
244 	ARPHRD_PPP,
245 	syncppp_init,
246 	syncppp_exit,
247 	NULL
248 };
249 
250 
251 #ifdef MODULE
252 #define comx_proto_ppp_init init_module
253 #endif
254 
comx_proto_ppp_init(void)255 int __init comx_proto_ppp_init(void)
256 {
257 	int ret;
258 
259 	if(0!=(ret=comx_register_protocol(&hdlc_protocol))) {
260 		return ret;
261 	}
262 	return comx_register_protocol(&syncppp_protocol);
263 }
264 
265 #ifdef MODULE
cleanup_module(void)266 void cleanup_module(void)
267 {
268 	comx_unregister_protocol(syncppp_protocol.name);
269 	comx_unregister_protocol(hdlc_protocol.name);
270 }
271 #endif /* MODULE */
272 
273