📄 x25_asy.c
字号:
/* * Things to sort out: * * o tbusy handling * o allow users to set the parameters * o sync/async switching ? * * Note: This does _not_ implement CCITT X.25 asynchronous framing * recommendations. Its primarily for testing purposes. If you wanted * to do CCITT then in theory all you need is to nick the HDLC async * checksum routines from ppp.c */#include <linux/module.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/in.h>#include <linux/tty.h>#include <linux/errno.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/if_arp.h>#include <linux/x25.h>#include <linux/lapb.h>#include <linux/init.h>#include "x25_asy.h"typedef struct x25_ctrl { char if_name[8]; /* "xasy0\0" .. "xasy99999\0" */ struct x25_asy ctrl; /* X.25 things */ struct device dev; /* the device */} x25_asy_ctrl_t;static x25_asy_ctrl_t **x25_asy_ctrls = NULL;int x25_asy_maxdev = SL_NRUNIT; /* Can be overridden with insmod! */MODULE_PARM(x25_asy_maxdev, "i");static struct tty_ldisc x25_ldisc;static int x25_asy_esc(unsigned char *p, unsigned char *d, int len);static void x25_asy_unesc(struct x25_asy *sl, unsigned char c);/* Find a free X.25 channel, and link in this `tty' line. */static inline struct x25_asy *x25_asy_alloc(void){ x25_asy_ctrl_t *slp = NULL; int i; if (x25_asy_ctrls == NULL) return NULL; /* Master array missing ! */ for (i = 0; i < x25_asy_maxdev; i++) { slp = x25_asy_ctrls[i]; /* Not allocated ? */ if (slp == NULL) break; /* Not in use ? */ if (!test_and_set_bit(SLF_INUSE, &slp->ctrl.flags)) break; } /* SLP is set.. */ /* Sorry, too many, all slots in use */ if (i >= x25_asy_maxdev) return NULL; /* If no channels are available, allocate one */ if (!slp && (x25_asy_ctrls[i] = (x25_asy_ctrl_t *)kmalloc(sizeof(x25_asy_ctrl_t), GFP_KERNEL)) != NULL) { slp = x25_asy_ctrls[i]; memset(slp, 0, sizeof(x25_asy_ctrl_t)); /* Initialize channel control data */ set_bit(SLF_INUSE, &slp->ctrl.flags); slp->ctrl.tty = NULL; sprintf(slp->if_name, "x25asy%d", i); slp->dev.name = slp->if_name; slp->dev.base_addr = i; slp->dev.priv = (void*)&(slp->ctrl); slp->dev.next = NULL; slp->dev.init = x25_asy_init; } if (slp != NULL) { /* register device so that it can be ifconfig'ed */ /* x25_asy_init() will be called as a side-effect */ /* SIDE-EFFECT WARNING: x25_asy_init() CLEARS slp->ctrl ! */ if (register_netdev(&(slp->dev)) == 0) { /* (Re-)Set the INUSE bit. Very Important! */ set_bit(SLF_INUSE, &slp->ctrl.flags); slp->ctrl.dev = &(slp->dev); slp->dev.priv = (void*)&(slp->ctrl); return (&(slp->ctrl)); } else { clear_bit(SLF_INUSE,&(slp->ctrl.flags)); printk("x25_asy_alloc() - register_netdev() failure.\n"); } } return NULL;}/* Free an X.25 channel. */static inline void x25_asy_free(struct x25_asy *sl){ /* Free all X.25 frame buffers. */ if (sl->rbuff) { kfree(sl->rbuff); } sl->rbuff = NULL; if (sl->xbuff) { kfree(sl->xbuff); } sl->xbuff = NULL; if (!test_and_clear_bit(SLF_INUSE, &sl->flags)) { printk("%s: x25_asy_free for already free unit.\n", sl->dev->name); }}/* MTU has been changed by the IP layer. Unfortunately we are not told about this, but we spot it ourselves and fix things up. We could be in an upcall from the tty driver, or in an ip packet queue. */static void x25_asy_changed_mtu(struct x25_asy *sl){ struct device *dev = sl->dev; unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; int len; unsigned long flags; len = dev->mtu * 2; xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); if (xbuff == NULL || rbuff == NULL) { printk("%s: unable to grow X.25 buffers, MTU change cancelled.\n", sl->dev->name); dev->mtu = sl->mtu; if (xbuff != NULL) kfree(xbuff); if (rbuff != NULL) kfree(rbuff); return; } save_flags(flags); cli(); oxbuff = sl->xbuff; sl->xbuff = xbuff; orbuff = sl->rbuff; sl->rbuff = rbuff; if (sl->xleft) { if (sl->xleft <= len) { memcpy(sl->xbuff, sl->xhead, sl->xleft); } else { sl->xleft = 0; sl->tx_dropped++; } } sl->xhead = sl->xbuff; if (sl->rcount) { if (sl->rcount <= len) { memcpy(sl->rbuff, orbuff, sl->rcount); } else { sl->rcount = 0; sl->rx_over_errors++; set_bit(SLF_ERROR, &sl->flags); } } sl->mtu = dev->mtu; sl->buffsize = len; restore_flags(flags); if (oxbuff != NULL) kfree(oxbuff); if (orbuff != NULL) kfree(orbuff);}/* Set the "sending" flag. This must be atomic, hence the ASM. */static inline void x25_asy_lock(struct x25_asy *sl){ if (test_and_set_bit(0, (void *) &sl->dev->tbusy)) printk("%s: trying to lock already locked device!\n", sl->dev->name);}/* Clear the "sending" flag. This must be atomic, hence the ASM. */static inline void x25_asy_unlock(struct x25_asy *sl){ if (!test_and_clear_bit(0, (void *)&sl->dev->tbusy)) printk("%s: trying to unlock already unlocked device!\n", sl->dev->name);}/* Send one completely decapsulated IP datagram to the IP layer. */static void x25_asy_bump(struct x25_asy *sl){ struct sk_buff *skb; int count; int err; count = sl->rcount; sl->rx_bytes+=count; skb = dev_alloc_skb(count+1); if (skb == NULL) { printk("%s: memory squeeze, dropping packet.\n", sl->dev->name); sl->rx_dropped++; return; } skb_push(skb,1); /* LAPB internal control */ skb->dev = sl->dev; memcpy(skb_put(skb,count), sl->rbuff, count); skb->mac.raw=skb->data; skb->protocol=htons(ETH_P_X25); if((err=lapb_data_received(sl,skb))!=LAPB_OK) { kfree_skb(skb); printk(KERN_DEBUG "x25_asy: data received err - %d\n",err); } else { netif_rx(skb); sl->rx_packets++; }}/* Encapsulate one IP datagram and stuff into a TTY queue. */static void x25_asy_encaps(struct x25_asy *sl, unsigned char *icp, int len){ unsigned char *p; int actual, count; if (sl->mtu != sl->dev->mtu) { /* Someone has been ifconfigging */ x25_asy_changed_mtu(sl); } if (len > sl->mtu) { /* Sigh, shouldn't occur BUT ... */ len = sl->mtu; printk ("%s: truncating oversized transmit packet!\n", sl->dev->name); sl->tx_dropped++; x25_asy_unlock(sl); return; } p = icp; count = x25_asy_esc(p, (unsigned char *) sl->xbuff, len); /* Order of next two lines is *very* important. * When we are sending a little amount of data, * the transfer may be completed inside driver.write() * routine, because it's running with interrupts enabled. * In this case we *never* got WRITE_WAKEUP event, * if we did not request it before write operation. * 14 Oct 1994 Dmitry Gorodchanin. */ sl->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); actual = sl->tty->driver.write(sl->tty, 0, sl->xbuff, count); sl->xleft = count - actual; sl->xhead = sl->xbuff + actual; /* VSV */ clear_bit(SLF_OUTWAIT, &sl->flags); /* reset outfill flag */}/* * Called by the driver when there's room for more data. If we have * more packets to send, we send them here. */static void x25_asy_write_wakeup(struct tty_struct *tty){ int actual; struct x25_asy *sl = (struct x25_asy *) tty->disc_data; /* First make sure we're connected. */ if (!sl || sl->magic != X25_ASY_MAGIC || !sl->dev->start) return; if (sl->xleft <= 0) { /* Now serial buffer is almost free & we can start * transmission of another packet */ sl->tx_packets++; tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); x25_asy_unlock(sl); mark_bh(NET_BH); return; } actual = tty->driver.write(tty, 0, sl->xhead, sl->xleft); sl->xleft -= actual; sl->xhead += actual;}/* Encapsulate an IP datagram and kick it into a TTY queue. */static int x25_asy_xmit(struct sk_buff *skb, struct device *dev){ struct x25_asy *sl = (struct x25_asy*)(dev->priv); int err; if (!dev->start) { printk("%s: xmit call when iface is down\n", dev->name); return 1; } switch(skb->data[0]) { case 0x00:break; case 0x01: /* Connection request .. do nothing */ if((err=lapb_connect_request(sl))!=LAPB_OK) printk(KERN_ERR "x25_asy: lapb_connect_request error - %d\n", err); kfree_skb(skb); return 0; case 0x02: /* Disconnect request .. do nothing - hang up ?? */ if((err=lapb_disconnect_request(sl))!=LAPB_OK) printk(KERN_ERR "x25_asy: lapb_disconnect_request error - %d\n", err); default: kfree_skb(skb); return 0; } skb_pull(skb,1); /* Remove control byte */ /* * If we are busy already- too bad. We ought to be able * to queue things at this point, to allow for a little * frame buffer. Oh well... * ----------------------------------------------------- * I hate queues in X.25 driver. May be it's efficient, * but for me latency is more important. ;) * So, no queues ! * 14 Oct 1994 Dmitry Gorodchanin. */ if (dev->tbusy) { /* May be we must check transmitter timeout here ? * 14 Oct 1994 Dmitry Gorodchanin. */#ifdef SL_CHECK_TRANSMIT if (jiffies - dev->trans_start < 20 * HZ) { /* 20 sec timeout not reached */ return 1; } printk("%s: transmit timed out, %s?\n", dev->name, (sl->tty->driver.chars_in_buffer(sl->tty) || sl->xleft) ? "bad line quality" : "driver error"); sl->xleft = 0; sl->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); x25_asy_unlock(sl);#else return 1;#endif } if((err=lapb_data_request(sl,skb))!=LAPB_OK) { printk(KERN_ERR "lapbeth: lapb_data_request error - %d\n", err); kfree_skb(skb); return 0; } return 0;}/* * LAPB interface boilerplate *//* * Called when I frame data arrives. We did the work above - throw it * at the net layer. */ static void x25_asy_data_indication(void *token, struct sk_buff *skb){ netif_rx(skb);}/* * Data has emerged from the LAPB protocol machine. We don't handle * busy cases too well. Its tricky to see how to do this nicely - * perhaps lapb should allow us to bounce this ? */ static void x25_asy_data_transmit(void *token, struct sk_buff *skb){ struct x25_asy *sl=token; if(sl->dev->tbusy) { printk(KERN_ERR "x25_asy: tbusy drop\n"); kfree_skb(skb); return; } /* We were not busy, so we are now... :-) */ if (skb != NULL) { x25_asy_lock(sl); sl->tx_bytes+=skb->len; x25_asy_encaps(sl, skb->data, skb->len); dev_kfree_skb(skb); }}/* * LAPB connection establish/down information. */ static void x25_asy_connected(void *token, int reason){ struct x25_asy *sl = token; struct sk_buff *skb; unsigned char *ptr; if ((skb = dev_alloc_skb(1)) == NULL) { printk(KERN_ERR "lapbeth: out of memory\n"); return; } ptr = skb_put(skb, 1); *ptr = 0x01; skb->dev = sl->dev; skb->protocol = htons(ETH_P_X25); skb->mac.raw = skb->data; skb->pkt_type = PACKET_HOST; netif_rx(skb);}static void x25_asy_disconnected(void *token, int reason){ struct x25_asy *sl = token; struct sk_buff *skb; unsigned char *ptr; if ((skb = dev_alloc_skb(1)) == NULL) { printk(KERN_ERR "x25_asy: out of memory\n"); return; } ptr = skb_put(skb, 1); *ptr = 0x02; skb->dev = sl->dev;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -