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/x25_asy.c |
Linux-2.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/x25_asy.c')
-rw-r--r-- | drivers/net/wan/x25_asy.c | 844 |
1 files changed, 844 insertions, 0 deletions
diff --git a/drivers/net/wan/x25_asy.c b/drivers/net/wan/x25_asy.c new file mode 100644 index 000000000000..8c5cfcb55826 --- /dev/null +++ b/drivers/net/wan/x25_asy.c | |||
@@ -0,0 +1,844 @@ | |||
1 | /* | ||
2 | * Things to sort out: | ||
3 | * | ||
4 | * o tbusy handling | ||
5 | * o allow users to set the parameters | ||
6 | * o sync/async switching ? | ||
7 | * | ||
8 | * Note: This does _not_ implement CCITT X.25 asynchronous framing | ||
9 | * recommendations. Its primarily for testing purposes. If you wanted | ||
10 | * to do CCITT then in theory all you need is to nick the HDLC async | ||
11 | * checksum routines from ppp.c | ||
12 | * Changes: | ||
13 | * | ||
14 | * 2000-10-29 Henner Eisen lapb_data_indication() return status. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | |||
19 | #include <asm/system.h> | ||
20 | #include <asm/uaccess.h> | ||
21 | #include <linux/bitops.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/in.h> | ||
26 | #include <linux/tty.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/netdevice.h> | ||
29 | #include <linux/etherdevice.h> | ||
30 | #include <linux/skbuff.h> | ||
31 | #include <linux/if_arp.h> | ||
32 | #include <linux/x25.h> | ||
33 | #include <linux/lapb.h> | ||
34 | #include <linux/init.h> | ||
35 | #include "x25_asy.h" | ||
36 | |||
37 | #include <net/x25device.h> | ||
38 | |||
39 | static struct net_device **x25_asy_devs; | ||
40 | static int x25_asy_maxdev = SL_NRUNIT; | ||
41 | |||
42 | module_param(x25_asy_maxdev, int, 0); | ||
43 | MODULE_LICENSE("GPL"); | ||
44 | |||
45 | static int x25_asy_esc(unsigned char *p, unsigned char *d, int len); | ||
46 | static void x25_asy_unesc(struct x25_asy *sl, unsigned char c); | ||
47 | static void x25_asy_setup(struct net_device *dev); | ||
48 | |||
49 | /* Find a free X.25 channel, and link in this `tty' line. */ | ||
50 | static struct x25_asy *x25_asy_alloc(void) | ||
51 | { | ||
52 | struct net_device *dev = NULL; | ||
53 | struct x25_asy *sl; | ||
54 | int i; | ||
55 | |||
56 | if (x25_asy_devs == NULL) | ||
57 | return NULL; /* Master array missing ! */ | ||
58 | |||
59 | for (i = 0; i < x25_asy_maxdev; i++) { | ||
60 | dev = x25_asy_devs[i]; | ||
61 | |||
62 | /* Not allocated ? */ | ||
63 | if (dev == NULL) | ||
64 | break; | ||
65 | |||
66 | sl = dev->priv; | ||
67 | /* Not in use ? */ | ||
68 | if (!test_and_set_bit(SLF_INUSE, &sl->flags)) | ||
69 | return sl; | ||
70 | } | ||
71 | |||
72 | |||
73 | /* Sorry, too many, all slots in use */ | ||
74 | if (i >= x25_asy_maxdev) | ||
75 | return NULL; | ||
76 | |||
77 | /* If no channels are available, allocate one */ | ||
78 | if (!dev) { | ||
79 | char name[IFNAMSIZ]; | ||
80 | sprintf(name, "x25asy%d", i); | ||
81 | |||
82 | dev = alloc_netdev(sizeof(struct x25_asy), | ||
83 | name, x25_asy_setup); | ||
84 | if (!dev) | ||
85 | return NULL; | ||
86 | |||
87 | /* Initialize channel control data */ | ||
88 | sl = dev->priv; | ||
89 | dev->base_addr = i; | ||
90 | |||
91 | /* register device so that it can be ifconfig'ed */ | ||
92 | if (register_netdev(dev) == 0) { | ||
93 | /* (Re-)Set the INUSE bit. Very Important! */ | ||
94 | set_bit(SLF_INUSE, &sl->flags); | ||
95 | x25_asy_devs[i] = dev; | ||
96 | return sl; | ||
97 | } else { | ||
98 | printk("x25_asy_alloc() - register_netdev() failure.\n"); | ||
99 | free_netdev(dev); | ||
100 | } | ||
101 | } | ||
102 | return NULL; | ||
103 | } | ||
104 | |||
105 | |||
106 | /* Free an X.25 channel. */ | ||
107 | static void x25_asy_free(struct x25_asy *sl) | ||
108 | { | ||
109 | /* Free all X.25 frame buffers. */ | ||
110 | if (sl->rbuff) { | ||
111 | kfree(sl->rbuff); | ||
112 | } | ||
113 | sl->rbuff = NULL; | ||
114 | if (sl->xbuff) { | ||
115 | kfree(sl->xbuff); | ||
116 | } | ||
117 | sl->xbuff = NULL; | ||
118 | |||
119 | if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) { | ||
120 | printk("%s: x25_asy_free for already free unit.\n", sl->dev->name); | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static int x25_asy_change_mtu(struct net_device *dev, int newmtu) | ||
125 | { | ||
126 | struct x25_asy *sl = dev->priv; | ||
127 | unsigned char *xbuff, *rbuff; | ||
128 | int len = 2* newmtu; | ||
129 | |||
130 | xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); | ||
131 | rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); | ||
132 | |||
133 | if (xbuff == NULL || rbuff == NULL) | ||
134 | { | ||
135 | printk("%s: unable to grow X.25 buffers, MTU change cancelled.\n", | ||
136 | dev->name); | ||
137 | if (xbuff != NULL) | ||
138 | kfree(xbuff); | ||
139 | if (rbuff != NULL) | ||
140 | kfree(rbuff); | ||
141 | return -ENOMEM; | ||
142 | } | ||
143 | |||
144 | spin_lock_bh(&sl->lock); | ||
145 | xbuff = xchg(&sl->xbuff, xbuff); | ||
146 | if (sl->xleft) { | ||
147 | if (sl->xleft <= len) { | ||
148 | memcpy(sl->xbuff, sl->xhead, sl->xleft); | ||
149 | } else { | ||
150 | sl->xleft = 0; | ||
151 | sl->stats.tx_dropped++; | ||
152 | } | ||
153 | } | ||
154 | sl->xhead = sl->xbuff; | ||
155 | |||
156 | rbuff = xchg(&sl->rbuff, rbuff); | ||
157 | if (sl->rcount) { | ||
158 | if (sl->rcount <= len) { | ||
159 | memcpy(sl->rbuff, rbuff, sl->rcount); | ||
160 | } else { | ||
161 | sl->rcount = 0; | ||
162 | sl->stats.rx_over_errors++; | ||
163 | set_bit(SLF_ERROR, &sl->flags); | ||
164 | } | ||
165 | } | ||
166 | |||
167 | dev->mtu = newmtu; | ||
168 | sl->buffsize = len; | ||
169 | |||
170 | spin_unlock_bh(&sl->lock); | ||
171 | |||
172 | if (xbuff != NULL) | ||
173 | kfree(xbuff); | ||
174 | if (rbuff != NULL) | ||
175 | kfree(rbuff); | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | |||
180 | /* Set the "sending" flag. This must be atomic, hence the ASM. */ | ||
181 | |||
182 | static inline void x25_asy_lock(struct x25_asy *sl) | ||
183 | { | ||
184 | netif_stop_queue(sl->dev); | ||
185 | } | ||
186 | |||
187 | |||
188 | /* Clear the "sending" flag. This must be atomic, hence the ASM. */ | ||
189 | |||
190 | static inline void x25_asy_unlock(struct x25_asy *sl) | ||
191 | { | ||
192 | netif_wake_queue(sl->dev); | ||
193 | } | ||
194 | |||
195 | /* Send one completely decapsulated IP datagram to the IP layer. */ | ||
196 | |||
197 | static void x25_asy_bump(struct x25_asy *sl) | ||
198 | { | ||
199 | struct sk_buff *skb; | ||
200 | int count; | ||
201 | int err; | ||
202 | |||
203 | count = sl->rcount; | ||
204 | sl->stats.rx_bytes+=count; | ||
205 | |||
206 | skb = dev_alloc_skb(count+1); | ||
207 | if (skb == NULL) | ||
208 | { | ||
209 | printk("%s: memory squeeze, dropping packet.\n", sl->dev->name); | ||
210 | sl->stats.rx_dropped++; | ||
211 | return; | ||
212 | } | ||
213 | skb_push(skb,1); /* LAPB internal control */ | ||
214 | memcpy(skb_put(skb,count), sl->rbuff, count); | ||
215 | skb->protocol = x25_type_trans(skb, sl->dev); | ||
216 | if((err=lapb_data_received(skb->dev, skb))!=LAPB_OK) | ||
217 | { | ||
218 | kfree_skb(skb); | ||
219 | printk(KERN_DEBUG "x25_asy: data received err - %d\n",err); | ||
220 | } | ||
221 | else | ||
222 | { | ||
223 | netif_rx(skb); | ||
224 | sl->dev->last_rx = jiffies; | ||
225 | sl->stats.rx_packets++; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | /* Encapsulate one IP datagram and stuff into a TTY queue. */ | ||
230 | static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len) | ||
231 | { | ||
232 | unsigned char *p; | ||
233 | int actual, count, mtu = sl->dev->mtu; | ||
234 | |||
235 | if (len > mtu) | ||
236 | { /* Sigh, shouldn't occur BUT ... */ | ||
237 | len = mtu; | ||
238 | printk ("%s: truncating oversized transmit packet!\n", sl->dev->name); | ||
239 | sl->stats.tx_dropped++; | ||
240 | x25_asy_unlock(sl); | ||
241 | return; | ||
242 | } | ||
243 | |||
244 | p = icp; | ||
245 | count = x25_asy_esc(p, (unsigned char *) sl->xbuff, len); | ||
246 | |||
247 | /* Order of next two lines is *very* important. | ||
248 | * When we are sending a little amount of data, | ||
249 | * the transfer may be completed inside driver.write() | ||
250 | * routine, because it's running with interrupts enabled. | ||
251 | * In this case we *never* got WRITE_WAKEUP event, | ||
252 | * if we did not request it before write operation. | ||
253 | * 14 Oct 1994 Dmitry Gorodchanin. | ||
254 | */ | ||
255 | sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); | ||
256 | actual = sl->tty->driver->write(sl->tty, sl->xbuff, count); | ||
257 | sl->xleft = count - actual; | ||
258 | sl->xhead = sl->xbuff + actual; | ||
259 | /* VSV */ | ||
260 | clear_bit(SLF_OUTWAIT, &sl->flags); /* reset outfill flag */ | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Called by the driver when there's room for more data. If we have | ||
265 | * more packets to send, we send them here. | ||
266 | */ | ||
267 | static void x25_asy_write_wakeup(struct tty_struct *tty) | ||
268 | { | ||
269 | int actual; | ||
270 | struct x25_asy *sl = (struct x25_asy *) tty->disc_data; | ||
271 | |||
272 | /* First make sure we're connected. */ | ||
273 | if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev)) | ||
274 | return; | ||
275 | |||
276 | if (sl->xleft <= 0) | ||
277 | { | ||
278 | /* Now serial buffer is almost free & we can start | ||
279 | * transmission of another packet */ | ||
280 | sl->stats.tx_packets++; | ||
281 | tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); | ||
282 | x25_asy_unlock(sl); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | actual = tty->driver->write(tty, sl->xhead, sl->xleft); | ||
287 | sl->xleft -= actual; | ||
288 | sl->xhead += actual; | ||
289 | } | ||
290 | |||
291 | static void x25_asy_timeout(struct net_device *dev) | ||
292 | { | ||
293 | struct x25_asy *sl = (struct x25_asy*)(dev->priv); | ||
294 | |||
295 | spin_lock(&sl->lock); | ||
296 | if (netif_queue_stopped(dev)) { | ||
297 | /* May be we must check transmitter timeout here ? | ||
298 | * 14 Oct 1994 Dmitry Gorodchanin. | ||
299 | */ | ||
300 | printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, | ||
301 | (sl->tty->driver->chars_in_buffer(sl->tty) || sl->xleft) ? | ||
302 | "bad line quality" : "driver error"); | ||
303 | sl->xleft = 0; | ||
304 | sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); | ||
305 | x25_asy_unlock(sl); | ||
306 | } | ||
307 | spin_unlock(&sl->lock); | ||
308 | } | ||
309 | |||
310 | /* Encapsulate an IP datagram and kick it into a TTY queue. */ | ||
311 | |||
312 | static int x25_asy_xmit(struct sk_buff *skb, struct net_device *dev) | ||
313 | { | ||
314 | struct x25_asy *sl = (struct x25_asy*)(dev->priv); | ||
315 | int err; | ||
316 | |||
317 | if (!netif_running(sl->dev)) { | ||
318 | printk("%s: xmit call when iface is down\n", dev->name); | ||
319 | kfree_skb(skb); | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | switch(skb->data[0]) | ||
324 | { | ||
325 | case 0x00:break; | ||
326 | case 0x01: /* Connection request .. do nothing */ | ||
327 | if((err=lapb_connect_request(dev))!=LAPB_OK) | ||
328 | printk(KERN_ERR "x25_asy: lapb_connect_request error - %d\n", err); | ||
329 | kfree_skb(skb); | ||
330 | return 0; | ||
331 | case 0x02: /* Disconnect request .. do nothing - hang up ?? */ | ||
332 | if((err=lapb_disconnect_request(dev))!=LAPB_OK) | ||
333 | printk(KERN_ERR "x25_asy: lapb_disconnect_request error - %d\n", err); | ||
334 | default: | ||
335 | kfree_skb(skb); | ||
336 | return 0; | ||
337 | } | ||
338 | skb_pull(skb,1); /* Remove control byte */ | ||
339 | /* | ||
340 | * If we are busy already- too bad. We ought to be able | ||
341 | * to queue things at this point, to allow for a little | ||
342 | * frame buffer. Oh well... | ||
343 | * ----------------------------------------------------- | ||
344 | * I hate queues in X.25 driver. May be it's efficient, | ||
345 | * but for me latency is more important. ;) | ||
346 | * So, no queues ! | ||
347 | * 14 Oct 1994 Dmitry Gorodchanin. | ||
348 | */ | ||
349 | |||
350 | if((err=lapb_data_request(dev,skb))!=LAPB_OK) | ||
351 | { | ||
352 | printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err); | ||
353 | kfree_skb(skb); | ||
354 | return 0; | ||
355 | } | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | |||
360 | /* | ||
361 | * LAPB interface boilerplate | ||
362 | */ | ||
363 | |||
364 | /* | ||
365 | * Called when I frame data arrives. We did the work above - throw it | ||
366 | * at the net layer. | ||
367 | */ | ||
368 | |||
369 | static int x25_asy_data_indication(struct net_device *dev, struct sk_buff *skb) | ||
370 | { | ||
371 | skb->dev->last_rx = jiffies; | ||
372 | return netif_rx(skb); | ||
373 | } | ||
374 | |||
375 | /* | ||
376 | * Data has emerged from the LAPB protocol machine. We don't handle | ||
377 | * busy cases too well. Its tricky to see how to do this nicely - | ||
378 | * perhaps lapb should allow us to bounce this ? | ||
379 | */ | ||
380 | |||
381 | static void x25_asy_data_transmit(struct net_device *dev, struct sk_buff *skb) | ||
382 | { | ||
383 | struct x25_asy *sl=dev->priv; | ||
384 | |||
385 | spin_lock(&sl->lock); | ||
386 | if (netif_queue_stopped(sl->dev) || sl->tty == NULL) | ||
387 | { | ||
388 | spin_unlock(&sl->lock); | ||
389 | printk(KERN_ERR "x25_asy: tbusy drop\n"); | ||
390 | kfree_skb(skb); | ||
391 | return; | ||
392 | } | ||
393 | /* We were not busy, so we are now... :-) */ | ||
394 | if (skb != NULL) | ||
395 | { | ||
396 | x25_asy_lock(sl); | ||
397 | sl->stats.tx_bytes+=skb->len; | ||
398 | x25_asy_encaps(sl, skb->data, skb->len); | ||
399 | dev_kfree_skb(skb); | ||
400 | } | ||
401 | spin_unlock(&sl->lock); | ||
402 | } | ||
403 | |||
404 | /* | ||
405 | * LAPB connection establish/down information. | ||
406 | */ | ||
407 | |||
408 | static void x25_asy_connected(struct net_device *dev, int reason) | ||
409 | { | ||
410 | struct x25_asy *sl = dev->priv; | ||
411 | struct sk_buff *skb; | ||
412 | unsigned char *ptr; | ||
413 | |||
414 | if ((skb = dev_alloc_skb(1)) == NULL) { | ||
415 | printk(KERN_ERR "lapbeth: out of memory\n"); | ||
416 | return; | ||
417 | } | ||
418 | |||
419 | ptr = skb_put(skb, 1); | ||
420 | *ptr = 0x01; | ||
421 | |||
422 | skb->protocol = x25_type_trans(skb, sl->dev); | ||
423 | netif_rx(skb); | ||
424 | sl->dev->last_rx = jiffies; | ||
425 | } | ||
426 | |||
427 | static void x25_asy_disconnected(struct net_device *dev, int reason) | ||
428 | { | ||
429 | struct x25_asy *sl = dev->priv; | ||
430 | struct sk_buff *skb; | ||
431 | unsigned char *ptr; | ||
432 | |||
433 | if ((skb = dev_alloc_skb(1)) == NULL) { | ||
434 | printk(KERN_ERR "x25_asy: out of memory\n"); | ||
435 | return; | ||
436 | } | ||
437 | |||
438 | ptr = skb_put(skb, 1); | ||
439 | *ptr = 0x02; | ||
440 | |||
441 | skb->protocol = x25_type_trans(skb, sl->dev); | ||
442 | netif_rx(skb); | ||
443 | sl->dev->last_rx = jiffies; | ||
444 | } | ||
445 | |||
446 | static struct lapb_register_struct x25_asy_callbacks = { | ||
447 | .connect_confirmation = x25_asy_connected, | ||
448 | .connect_indication = x25_asy_connected, | ||
449 | .disconnect_confirmation = x25_asy_disconnected, | ||
450 | .disconnect_indication = x25_asy_disconnected, | ||
451 | .data_indication = x25_asy_data_indication, | ||
452 | .data_transmit = x25_asy_data_transmit, | ||
453 | |||
454 | }; | ||
455 | |||
456 | |||
457 | /* Open the low-level part of the X.25 channel. Easy! */ | ||
458 | static int x25_asy_open(struct net_device *dev) | ||
459 | { | ||
460 | struct x25_asy *sl = (struct x25_asy*)(dev->priv); | ||
461 | unsigned long len; | ||
462 | int err; | ||
463 | |||
464 | if (sl->tty == NULL) | ||
465 | return -ENODEV; | ||
466 | |||
467 | /* | ||
468 | * Allocate the X.25 frame buffers: | ||
469 | * | ||
470 | * rbuff Receive buffer. | ||
471 | * xbuff Transmit buffer. | ||
472 | */ | ||
473 | |||
474 | len = dev->mtu * 2; | ||
475 | |||
476 | sl->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL); | ||
477 | if (sl->rbuff == NULL) { | ||
478 | goto norbuff; | ||
479 | } | ||
480 | sl->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL); | ||
481 | if (sl->xbuff == NULL) { | ||
482 | goto noxbuff; | ||
483 | } | ||
484 | |||
485 | sl->buffsize = len; | ||
486 | sl->rcount = 0; | ||
487 | sl->xleft = 0; | ||
488 | sl->flags &= (1 << SLF_INUSE); /* Clear ESCAPE & ERROR flags */ | ||
489 | |||
490 | netif_start_queue(dev); | ||
491 | |||
492 | /* | ||
493 | * Now attach LAPB | ||
494 | */ | ||
495 | if((err=lapb_register(dev, &x25_asy_callbacks))==LAPB_OK) | ||
496 | return 0; | ||
497 | |||
498 | /* Cleanup */ | ||
499 | kfree(sl->xbuff); | ||
500 | noxbuff: | ||
501 | kfree(sl->rbuff); | ||
502 | norbuff: | ||
503 | return -ENOMEM; | ||
504 | } | ||
505 | |||
506 | |||
507 | /* Close the low-level part of the X.25 channel. Easy! */ | ||
508 | static int x25_asy_close(struct net_device *dev) | ||
509 | { | ||
510 | struct x25_asy *sl = (struct x25_asy*)(dev->priv); | ||
511 | int err; | ||
512 | |||
513 | spin_lock(&sl->lock); | ||
514 | if (sl->tty) | ||
515 | sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); | ||
516 | |||
517 | netif_stop_queue(dev); | ||
518 | sl->rcount = 0; | ||
519 | sl->xleft = 0; | ||
520 | if((err=lapb_unregister(dev))!=LAPB_OK) | ||
521 | printk(KERN_ERR "x25_asy_close: lapb_unregister error -%d\n",err); | ||
522 | spin_unlock(&sl->lock); | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int x25_asy_receive_room(struct tty_struct *tty) | ||
527 | { | ||
528 | return 65536; /* We can handle an infinite amount of data. :-) */ | ||
529 | } | ||
530 | |||
531 | /* | ||
532 | * Handle the 'receiver data ready' interrupt. | ||
533 | * This function is called by the 'tty_io' module in the kernel when | ||
534 | * a block of X.25 data has been received, which can now be decapsulated | ||
535 | * and sent on to some IP layer for further processing. | ||
536 | */ | ||
537 | |||
538 | static void x25_asy_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) | ||
539 | { | ||
540 | struct x25_asy *sl = (struct x25_asy *) tty->disc_data; | ||
541 | |||
542 | if (!sl || sl->magic != X25_ASY_MAGIC || !netif_running(sl->dev)) | ||
543 | return; | ||
544 | |||
545 | |||
546 | /* Read the characters out of the buffer */ | ||
547 | while (count--) { | ||
548 | if (fp && *fp++) { | ||
549 | if (!test_and_set_bit(SLF_ERROR, &sl->flags)) { | ||
550 | sl->stats.rx_errors++; | ||
551 | } | ||
552 | cp++; | ||
553 | continue; | ||
554 | } | ||
555 | x25_asy_unesc(sl, *cp++); | ||
556 | } | ||
557 | } | ||
558 | |||
559 | /* | ||
560 | * Open the high-level part of the X.25 channel. | ||
561 | * This function is called by the TTY module when the | ||
562 | * X.25 line discipline is called for. Because we are | ||
563 | * sure the tty line exists, we only have to link it to | ||
564 | * a free X.25 channel... | ||
565 | */ | ||
566 | |||
567 | static int x25_asy_open_tty(struct tty_struct *tty) | ||
568 | { | ||
569 | struct x25_asy *sl = (struct x25_asy *) tty->disc_data; | ||
570 | int err; | ||
571 | |||
572 | /* First make sure we're not already connected. */ | ||
573 | if (sl && sl->magic == X25_ASY_MAGIC) { | ||
574 | return -EEXIST; | ||
575 | } | ||
576 | |||
577 | /* OK. Find a free X.25 channel to use. */ | ||
578 | if ((sl = x25_asy_alloc()) == NULL) { | ||
579 | return -ENFILE; | ||
580 | } | ||
581 | |||
582 | sl->tty = tty; | ||
583 | tty->disc_data = sl; | ||
584 | if (tty->driver->flush_buffer) { | ||
585 | tty->driver->flush_buffer(tty); | ||
586 | } | ||
587 | if (tty->ldisc.flush_buffer) { | ||
588 | tty->ldisc.flush_buffer(tty); | ||
589 | } | ||
590 | |||
591 | /* Restore default settings */ | ||
592 | sl->dev->type = ARPHRD_X25; | ||
593 | |||
594 | /* Perform the low-level X.25 async init */ | ||
595 | if ((err = x25_asy_open(sl->dev))) | ||
596 | return err; | ||
597 | |||
598 | /* Done. We have linked the TTY line to a channel. */ | ||
599 | return sl->dev->base_addr; | ||
600 | } | ||
601 | |||
602 | |||
603 | /* | ||
604 | * Close down an X.25 channel. | ||
605 | * This means flushing out any pending queues, and then restoring the | ||
606 | * TTY line discipline to what it was before it got hooked to X.25 | ||
607 | * (which usually is TTY again). | ||
608 | */ | ||
609 | static void x25_asy_close_tty(struct tty_struct *tty) | ||
610 | { | ||
611 | struct x25_asy *sl = (struct x25_asy *) tty->disc_data; | ||
612 | |||
613 | /* First make sure we're connected. */ | ||
614 | if (!sl || sl->magic != X25_ASY_MAGIC) | ||
615 | return; | ||
616 | |||
617 | if (sl->dev->flags & IFF_UP) | ||
618 | { | ||
619 | (void) dev_close(sl->dev); | ||
620 | } | ||
621 | |||
622 | tty->disc_data = NULL; | ||
623 | sl->tty = NULL; | ||
624 | x25_asy_free(sl); | ||
625 | } | ||
626 | |||
627 | |||
628 | static struct net_device_stats *x25_asy_get_stats(struct net_device *dev) | ||
629 | { | ||
630 | struct x25_asy *sl = (struct x25_asy*)(dev->priv); | ||
631 | |||
632 | return &sl->stats; | ||
633 | } | ||
634 | |||
635 | |||
636 | /************************************************************************ | ||
637 | * STANDARD X.25 ENCAPSULATION * | ||
638 | ************************************************************************/ | ||
639 | |||
640 | int x25_asy_esc(unsigned char *s, unsigned char *d, int len) | ||
641 | { | ||
642 | unsigned char *ptr = d; | ||
643 | unsigned char c; | ||
644 | |||
645 | /* | ||
646 | * Send an initial END character to flush out any | ||
647 | * data that may have accumulated in the receiver | ||
648 | * due to line noise. | ||
649 | */ | ||
650 | |||
651 | *ptr++ = X25_END; /* Send 10111110 bit seq */ | ||
652 | |||
653 | /* | ||
654 | * For each byte in the packet, send the appropriate | ||
655 | * character sequence, according to the X.25 protocol. | ||
656 | */ | ||
657 | |||
658 | while (len-- > 0) | ||
659 | { | ||
660 | switch(c = *s++) | ||
661 | { | ||
662 | case X25_END: | ||
663 | *ptr++ = X25_ESC; | ||
664 | *ptr++ = X25_ESCAPE(X25_END); | ||
665 | break; | ||
666 | case X25_ESC: | ||
667 | *ptr++ = X25_ESC; | ||
668 | *ptr++ = X25_ESCAPE(X25_ESC); | ||
669 | break; | ||
670 | default: | ||
671 | *ptr++ = c; | ||
672 | break; | ||
673 | } | ||
674 | } | ||
675 | *ptr++ = X25_END; | ||
676 | return (ptr - d); | ||
677 | } | ||
678 | |||
679 | static void x25_asy_unesc(struct x25_asy *sl, unsigned char s) | ||
680 | { | ||
681 | |||
682 | switch(s) | ||
683 | { | ||
684 | case X25_END: | ||
685 | if (!test_and_clear_bit(SLF_ERROR, &sl->flags) && (sl->rcount > 2)) | ||
686 | { | ||
687 | x25_asy_bump(sl); | ||
688 | } | ||
689 | clear_bit(SLF_ESCAPE, &sl->flags); | ||
690 | sl->rcount = 0; | ||
691 | return; | ||
692 | |||
693 | case X25_ESC: | ||
694 | set_bit(SLF_ESCAPE, &sl->flags); | ||
695 | return; | ||
696 | |||
697 | case X25_ESCAPE(X25_ESC): | ||
698 | case X25_ESCAPE(X25_END): | ||
699 | if (test_and_clear_bit(SLF_ESCAPE, &sl->flags)) | ||
700 | s = X25_UNESCAPE(s); | ||
701 | break; | ||
702 | } | ||
703 | if (!test_bit(SLF_ERROR, &sl->flags)) | ||
704 | { | ||
705 | if (sl->rcount < sl->buffsize) | ||
706 | { | ||
707 | sl->rbuff[sl->rcount++] = s; | ||
708 | return; | ||
709 | } | ||
710 | sl->stats.rx_over_errors++; | ||
711 | set_bit(SLF_ERROR, &sl->flags); | ||
712 | } | ||
713 | } | ||
714 | |||
715 | |||
716 | /* Perform I/O control on an active X.25 channel. */ | ||
717 | static int x25_asy_ioctl(struct tty_struct *tty, struct file *file, | ||
718 | unsigned int cmd, unsigned long arg) | ||
719 | { | ||
720 | struct x25_asy *sl = (struct x25_asy *) tty->disc_data; | ||
721 | |||
722 | /* First make sure we're connected. */ | ||
723 | if (!sl || sl->magic != X25_ASY_MAGIC) | ||
724 | return -EINVAL; | ||
725 | |||
726 | switch(cmd) { | ||
727 | case SIOCGIFNAME: | ||
728 | if (copy_to_user((void __user *)arg, sl->dev->name, | ||
729 | strlen(sl->dev->name) + 1)) | ||
730 | return -EFAULT; | ||
731 | return 0; | ||
732 | case SIOCSIFHWADDR: | ||
733 | return -EINVAL; | ||
734 | /* Allow stty to read, but not set, the serial port */ | ||
735 | case TCGETS: | ||
736 | case TCGETA: | ||
737 | return n_tty_ioctl(tty, file, cmd, arg); | ||
738 | default: | ||
739 | return -ENOIOCTLCMD; | ||
740 | } | ||
741 | } | ||
742 | |||
743 | static int x25_asy_open_dev(struct net_device *dev) | ||
744 | { | ||
745 | struct x25_asy *sl = (struct x25_asy*)(dev->priv); | ||
746 | if(sl->tty==NULL) | ||
747 | return -ENODEV; | ||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | /* Initialise the X.25 driver. Called by the device init code */ | ||
752 | static void x25_asy_setup(struct net_device *dev) | ||
753 | { | ||
754 | struct x25_asy *sl = dev->priv; | ||
755 | |||
756 | sl->magic = X25_ASY_MAGIC; | ||
757 | sl->dev = dev; | ||
758 | spin_lock_init(&sl->lock); | ||
759 | set_bit(SLF_INUSE, &sl->flags); | ||
760 | |||
761 | /* | ||
762 | * Finish setting up the DEVICE info. | ||
763 | */ | ||
764 | |||
765 | dev->mtu = SL_MTU; | ||
766 | dev->hard_start_xmit = x25_asy_xmit; | ||
767 | dev->tx_timeout = x25_asy_timeout; | ||
768 | dev->watchdog_timeo = HZ*20; | ||
769 | dev->open = x25_asy_open_dev; | ||
770 | dev->stop = x25_asy_close; | ||
771 | dev->get_stats = x25_asy_get_stats; | ||
772 | dev->change_mtu = x25_asy_change_mtu; | ||
773 | dev->hard_header_len = 0; | ||
774 | dev->addr_len = 0; | ||
775 | dev->type = ARPHRD_X25; | ||
776 | dev->tx_queue_len = 10; | ||
777 | |||
778 | /* New-style flags. */ | ||
779 | dev->flags = IFF_NOARP; | ||
780 | } | ||
781 | |||
782 | static struct tty_ldisc x25_ldisc = { | ||
783 | .owner = THIS_MODULE, | ||
784 | .magic = TTY_LDISC_MAGIC, | ||
785 | .name = "X.25", | ||
786 | .open = x25_asy_open_tty, | ||
787 | .close = x25_asy_close_tty, | ||
788 | .ioctl = x25_asy_ioctl, | ||
789 | .receive_buf = x25_asy_receive_buf, | ||
790 | .receive_room = x25_asy_receive_room, | ||
791 | .write_wakeup = x25_asy_write_wakeup, | ||
792 | }; | ||
793 | |||
794 | static int __init init_x25_asy(void) | ||
795 | { | ||
796 | if (x25_asy_maxdev < 4) | ||
797 | x25_asy_maxdev = 4; /* Sanity */ | ||
798 | |||
799 | printk(KERN_INFO "X.25 async: version 0.00 ALPHA " | ||
800 | "(dynamic channels, max=%d).\n", x25_asy_maxdev ); | ||
801 | |||
802 | x25_asy_devs = kmalloc(sizeof(struct net_device *)*x25_asy_maxdev, | ||
803 | GFP_KERNEL); | ||
804 | if (!x25_asy_devs) { | ||
805 | printk(KERN_WARNING "X25 async: Can't allocate x25_asy_ctrls[] " | ||
806 | "array! Uaargh! (-> No X.25 available)\n"); | ||
807 | return -ENOMEM; | ||
808 | } | ||
809 | memset(x25_asy_devs, 0, sizeof(struct net_device *)*x25_asy_maxdev); | ||
810 | |||
811 | return tty_register_ldisc(N_X25, &x25_ldisc); | ||
812 | } | ||
813 | |||
814 | |||
815 | static void __exit exit_x25_asy(void) | ||
816 | { | ||
817 | struct net_device *dev; | ||
818 | int i; | ||
819 | |||
820 | for (i = 0; i < x25_asy_maxdev; i++) { | ||
821 | dev = x25_asy_devs[i]; | ||
822 | if (dev) { | ||
823 | struct x25_asy *sl = dev->priv; | ||
824 | |||
825 | spin_lock_bh(&sl->lock); | ||
826 | if (sl->tty) | ||
827 | tty_hangup(sl->tty); | ||
828 | |||
829 | spin_unlock_bh(&sl->lock); | ||
830 | /* | ||
831 | * VSV = if dev->start==0, then device | ||
832 | * unregistered while close proc. | ||
833 | */ | ||
834 | unregister_netdev(dev); | ||
835 | free_netdev(dev); | ||
836 | } | ||
837 | } | ||
838 | |||
839 | kfree(x25_asy_devs); | ||
840 | tty_register_ldisc(N_X25, NULL); | ||
841 | } | ||
842 | |||
843 | module_init(init_x25_asy); | ||
844 | module_exit(exit_x25_asy); | ||