📄 slip.c
字号:
/* Add an ARP-entry for this device's broadcast address. Not used. */
static void
sl_add_arp(unsigned long addr, struct sk_buff *skb, struct device *dev)
{
#ifdef CONFIG_AX25
struct slip *sl=&sl_ctrl[dev->base_addr];
if(sl->mode&SL_MODE_AX25)
arp_add(addr,((char *) skb->data)+8,dev);
#endif
}
/* Rebuild the MAC-level header. Not used by SLIP. */
static int
sl_rebuild_header(void *buff, struct device *dev)
{
#ifdef CONFIG_AX25
struct slip *sl=&sl_ctrl[dev->base_addr];
if(sl->mode&SL_MODE_AX25)
return ax25_rebuild_header(buff,dev);
#endif
return(0);
}
/* Open the low-level part of the SLIP channel. Easy! */
static int
sl_open(struct device *dev)
{
struct slip *sl;
unsigned char *p;
unsigned long l;
sl = &sl_ctrl[dev->base_addr];
if (sl->tty == NULL) {
DPRINTF((DBG_SLIP, "SLIP: channel %d not connected!\n", sl->line));
return(-ENXIO);
}
sl->dev = dev;
/*
* Allocate the SLIP frame buffers:
*
* mem_end Top of frame buffers
* mem_start Start of frame buffers
* rmem_end Top of RECV frame buffer
* rmem_start Start of RECV frame buffer
*/
l = (dev->mtu * 2);
/*
* allow for arrival of larger UDP packets, even if we say not to
* also fixes a bug in which SunOS sends 512-byte packets even with
* an MSS of 128
*/
if (l < (576 * 2))
l = 576 * 2;
p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
if (p == NULL) {
DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP XMIT buffer!\n"));
return(-ENOMEM);
}
sl->mtu = dev->mtu;
sl->dev->mem_start = (unsigned long) p;
sl->dev->mem_end = (unsigned long) (sl->dev->mem_start + l);
p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
if (p == NULL) {
DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP RECV buffer!\n"));
return(-ENOMEM);
}
sl->dev->rmem_start = (unsigned long) p;
sl->dev->rmem_end = (unsigned long) (sl->dev->rmem_start + l);
sl->xbuff = (unsigned char *) sl->dev->mem_start;
sl->rbuff = (unsigned char *) sl->dev->rmem_start;
sl->rend = (unsigned char *) sl->dev->rmem_end;
sl->rhead = sl->rbuff;
sl->escape = 0;
sl->sending = 0;
sl->rcount = 0;
p = (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
if (p == NULL) {
kfree((unsigned char *)sl->dev->mem_start);
DPRINTF((DBG_SLIP, "SLIP: no memory for SLIP COMPRESS buffer!\n"));
return(-ENOMEM);
}
sl->cbuff = p;
sl->slcomp = slhc_init(16, 16);
if (sl->slcomp == NULL) {
kfree((unsigned char *)sl->dev->mem_start);
kfree((unsigned char *)sl->dev->rmem_start);
kfree(sl->cbuff);
DPRINTF((DBG_SLIP, "SLIP: no memory for SLCOMP!\n"));
return(-ENOMEM);
}
dev->flags|=IFF_UP;
/* Needed because address '0' is special */
if(dev->pa_addr==0)
dev->pa_addr=ntohl(0xC0000001);
DPRINTF((DBG_SLIP, "SLIP: channel %d opened.\n", sl->line));
return(0);
}
/* Close the low-level part of the SLIP channel. Easy! */
static int
sl_close(struct device *dev)
{
struct slip *sl;
sl = &sl_ctrl[dev->base_addr];
if (sl->tty == NULL) {
DPRINTF((DBG_SLIP, "SLIP: channel %d not connected!\n", sl->line));
return(-EBUSY);
}
sl_free(sl);
/* Free all SLIP frame buffers. */
kfree(sl->rbuff);
kfree(sl->xbuff);
kfree(sl->cbuff);
slhc_free(sl->slcomp);
sl_initialize(sl, dev);
DPRINTF((DBG_SLIP, "SLIP: channel %d closed.\n", sl->line));
return(0);
}
/*
* Handle the 'receiver data ready' interrupt.
* This function is called by the 'tty_io' module in the kernel when
* a block of SLIP data has been received, which can now be decapsulated
* and sent on to some IP layer for further processing.
*/
static void
slip_recv(struct tty_struct *tty)
{
unsigned char buff[128];
unsigned char *p;
struct slip *sl;
int count, error=0;
DPRINTF((DBG_SLIP, "SLIP: slip_recv(%d) called\n", tty->line));
if ((sl = sl_find(tty)) == NULL) return; /* not connected */
if(sl->mtu!=sl->dev->mtu) /* Argh! mtu change time! - costs us the packet part received at the change */
sl_changedmtu(sl);
/* Suck the bytes out of the TTY queues. */
do {
count = tty_read_raw_data(tty, buff, 128);
if (count <= 0)
{
count= - count;
if(count)
error=1;
break;
}
p = buff;
#ifdef OLD
while (count--) {
c = *p++;
if (sl->escape) {
if (c == ESC_ESC)
sl_enqueue(sl, ESC);
else if (c == ESC_END)
sl_enqueue(sl, END);
else
printk ("SLIP: received wrong character\n");
sl->escape = 0;
} else {
if (c == ESC)
sl->escape = 1;
else if (c == END) {
if (sl->rcount > 2) sl_bump(sl);
sl_dequeue(sl, sl->rcount);
sl->rcount = 0;
} else sl_enqueue(sl, c);
}
}
#else
if(sl->mode & SL_MODE_SLIP6)
slip_unesc6(sl,buff,count,error);
else
slip_unesc(sl,buff,count,error);
#endif
} while(1);
}
/*
* Open the high-level part of the SLIP channel.
* This function is called by the TTY module when the
* SLIP line discipline is called for. Because we are
* sure the tty line exists, we only have to link it to
* a free SLIP channel...
*/
static int
slip_open(struct tty_struct *tty)
{
struct slip *sl;
/* First make sure we're not already connected. */
if ((sl = sl_find(tty)) != NULL) {
DPRINTF((DBG_SLIP, "SLIP: TTY %d already connected to %s !\n",
tty->line, sl->dev->name));
return(-EEXIST);
}
/* OK. Find a free SLIP channel to use. */
if ((sl = sl_alloc()) == NULL) {
DPRINTF((DBG_SLIP, "SLIP: TTY %d not connected: all channels in use!\n",
tty->line));
return(-ENFILE);
}
sl->tty = tty;
tty_read_flush(tty);
tty_write_flush(tty);
/* Perform the low-level SLIP initialization. */
(void) sl_open(sl->dev);
DPRINTF((DBG_SLIP, "SLIP: TTY %d connected to %s.\n",
tty->line, sl->dev->name));
/* Done. We have linked the TTY line to a channel. */
return(sl->line);
}
static struct enet_statistics *
sl_get_stats(struct device *dev)
{
static struct enet_statistics stats;
struct slip *sl;
struct slcompress *comp;
/* Find the correct SLIP channel to use. */
sl = &sl_ctrl[dev->base_addr];
if (! sl)
return NULL;
memset(&stats, 0, sizeof(struct enet_statistics));
stats.rx_packets = sl->rpacket;
stats.rx_over_errors = sl->roverrun;
stats.tx_packets = sl->spacket;
stats.tx_dropped = sl->sbusy;
stats.rx_errors = sl->errors;
comp = sl->slcomp;
if (comp) {
stats.rx_fifo_errors = comp->sls_i_compressed;
stats.rx_dropped = comp->sls_i_tossed;
stats.tx_fifo_errors = comp->sls_o_compressed;
stats.collisions = comp->sls_o_misses;
}
return (&stats);
}
/*
* Close down a SLIP channel.
* This means flushing out any pending queues, and then restoring the
* TTY line discipline to what it was before it got hooked to SLIP
* (which usually is TTY again).
*/
static void
slip_close(struct tty_struct *tty)
{
struct slip *sl;
/* First make sure we're connected. */
if ((sl = sl_find(tty)) == NULL) {
DPRINTF((DBG_SLIP, "SLIP: TTY %d not connected !\n", tty->line));
return;
}
(void) dev_close(sl->dev);
DPRINTF((DBG_SLIP, "SLIP: TTY %d disconnected from %s.\n",
tty->line, sl->dev->name));
}
/************************************************************************
* STANDARD SLIP ENCAPSULATION *
************************************************************************
*
*/
int
slip_esc(unsigned char *s, unsigned char *d, int len)
{
int count = 0;
/*
* Send an initial END character to flush out any
* data that may have accumulated in the receiver
* due to line noise.
*/
d[count++] = END;
/*
* For each byte in the packet, send the appropriate
* character sequence, according to the SLIP protocol.
*/
while(len-- > 0) {
switch(*s) {
case END:
d[count++] = ESC;
d[count++] = ESC_END;
break;
case ESC:
d[count++] = ESC;
d[count++] = ESC_ESC;
break;
default:
d[count++] = *s;
}
++s;
}
d[count++] = END;
return(count);
}
void
slip_unesc(struct slip *sl, unsigned char *s, int count, int error)
{
int i;
for (i = 0; i < count; ++i, ++s) {
switch(*s) {
case ESC:
sl->flags |= SLF_ESCAPE;
break;
case ESC_ESC:
if (sl->flags & SLF_ESCAPE)
sl_enqueue(sl, ESC);
else
sl_enqueue(sl, *s);
sl->flags &= ~SLF_ESCAPE;
break;
case ESC_END:
if (sl->flags & SLF_ESCAPE)
sl_enqueue(sl, END);
else
sl_enqueue(sl, *s);
sl->flags &= ~SLF_ESCAPE;
break;
case END:
if (sl->rcount > 2)
sl_bump(sl);
sl_dequeue(sl, sl->rcount);
sl->rcount = 0;
sl->flags &= ~(SLF_ESCAPE | SLF_ERROR);
break;
default:
sl_enqueue(sl, *s);
sl->flags &= ~SLF_ESCAPE;
}
}
if (error)
sl->flags |= SLF_ERROR;
}
/************************************************************************
* 6 BIT SLIP ENCAPSULATION *
************************************************************************
*
*/
int
slip_esc6(unsigned char *s, unsigned char *d, int len)
{
int count = 0;
int i;
unsigned short v = 0;
short bits = 0;
/*
* Send an initial END character to flush out any
* data that may have accumulated in the receiver
* due to line noise.
*/
d[count++] = 0x70;
/*
* Encode the packet into printable ascii characters
*/
for (i = 0; i < len; ++i) {
v = (v << 8) | s[i];
bits += 8;
while (bits >= 6) {
unsigned char c;
bits -= 6;
c = 0x30 + ((v >> bits) & 0x3F);
d[count++] = c;
}
}
if (bits) {
unsigned char c;
c = 0x30 + ((v << (6 - bits)) & 0x3F);
d[count++] = c;
}
d[count++] = 0x70;
return(count);
}
void
slip_unesc6(struct slip *sl, unsigned char *s, int count, int error)
{
int i;
unsigned char c;
for (i = 0; i < count; ++i, ++s) {
if (*s == 0x70) {
if (sl->rcount > 8) { /* XXX must be 2 for compressed slip */
#ifdef NOTDEF
printk("rbuff %02x %02x %02x %02x\n",
sl->rbuff[0],
sl->rbuff[1],
sl->rbuff[2],
sl->rbuff[3]
);
#endif
sl_bump(sl);
}
sl_dequeue(sl, sl->rcount);
sl->rcount = 0;
sl->flags &= ~(SLF_ESCAPE | SLF_ERROR); /* SLF_ESCAPE not used */
sl->xbits = 0;
} else if (*s >= 0x30 && *s < 0x70) {
sl->xdata = (sl->xdata << 6) | ((*s - 0x30) & 0x3F);
sl->xbits += 6;
if (sl->xbits >= 8) {
sl->xbits -= 8;
c = (unsigned char)(sl->xdata >> sl->xbits);
sl_enqueue(sl, c);
}
}
}
if (error)
sl->flags |= SLF_ERROR;
}
#ifdef CONFIG_AX25
int sl_set_mac_address(struct device *dev, void *addr)
{
int err=verify_area(VERIFY_READ,addr,7);
if(err)
return err;
memcpy_fromfs(dev->dev_addr,addr,7); /* addr is an AX.25 shifted ASCII mac address */
return 0;
}
#endif
/* Perform I/O control on an active SLIP channel. */
static int
slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
{
struct slip *sl;
int err;
/* First make sure we're connected. */
if ((sl = sl_find(tty)) == NULL) {
DPRINTF((DBG_SLIP, "SLIP: ioctl: TTY %d not connected !\n", tty->line));
return(-EINVAL);
}
DPRINTF((DBG_SLIP, "SLIP: ioctl(%d, 0x%X, 0x%X)\n", tty->line, cmd, arg));
switch(cmd) {
case SIOCGIFNAME:
err=verify_area(VERIFY_WRITE, arg, 16);
if(err)
return -err;
memcpy_tofs(arg, sl->dev->name, strlen(sl->dev->name) + 1);
return(0);
case SIOCGIFENCAP:
err=verify_area(VERIFY_WRITE,arg,sizeof(long));
put_fs_long(sl->mode,(long *)arg);
return(0);
case SIOCSIFENCAP:
err=verify_area(VERIFY_READ,arg,sizeof(long));
sl->mode=get_fs_long((long *)arg);
#ifdef CONFIG_AX25
if(sl->mode & SL_MODE_AX25)
{
sl->dev->addr_len=7; /* sizeof an AX.25 addr */
sl->dev->hard_header_len=17; /* We don't do digipeaters */
sl->dev->type=3; /* AF_AX25 not an AF_INET device */
}
else
{
sl->dev->addr_len=0; /* No mac addr in slip mode */
sl->dev->hard_header_len=0;
sl->dev->type=0;
}
#endif
return(0);
case SIOCSIFHWADDR:
#ifdef CONFIG_AX25
return sl_set_mac_address(sl->dev,arg);
#endif
default:
return(-EINVAL);
}
return(-EINVAL);
}
/* Initialize the SLIP driver. Called by DDI. */
int
slip_init(struct device *dev)
{
struct slip *sl;
int i;
#ifdef CONFIG_AX25
static char ax25_bcast[7]={'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
static char ax25_test[7]={'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
#endif
sl = &sl_ctrl[dev->base_addr];
if (already++ == 0) {
printk("SLIP: version %s (%d channels)\n",
SLIP_VERSION, SL_NRUNIT);
printk("CSLIP: code copyright 1989 Regents of the University of California\n");
#ifdef CONFIG_AX25
printk("AX25: KISS encapsulation enabled\n");
#endif
/* Fill in our LDISC request block. */
sl_ldisc.flags = 0;
sl_ldisc.open = slip_open;
sl_ldisc.close = slip_close;
sl_ldisc.read = NULL;
sl_ldisc.write = NULL;
sl_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
unsigned int, unsigned long)) slip_ioctl;
sl_ldisc.select = NULL;
sl_ldisc.handler = slip_recv;
if ((i = tty_register_ldisc(N_SLIP, &sl_ldisc)) != 0)
printk("ERROR: %d\n", i);
}
/* Set up the "SLIP Control Block". */
sl_initialize(sl, dev);
/* Clear all statistics. */
sl->rcount = 0; /* SLIP receiver count */
sl->rpacket = 0; /* #frames received */
sl->roverrun = 0; /* "overrun" counter */
sl->spacket = 0; /* #frames sent out */
sl->sbusy = 0; /* "xmit busy" counter */
sl->errors = 0; /* not used at present */
/* Finish setting up the DEVICE info. */
dev->mtu = SL_MTU;
dev->hard_start_xmit = sl_xmit;
dev->open = sl_open;
dev->stop = sl_close;
dev->hard_header = sl_header;
dev->add_arp = sl_add_arp;
dev->type_trans = sl_type_trans;
dev->get_stats = sl_get_stats;
#ifdef HAVE_SET_MAC_ADDR
#ifdef CONFIG_AX25
dev->set_mac_address = sl_set_mac_address;
#endif
#endif
dev->hard_header_len = 0;
dev->addr_len = 0;
dev->type = 0;
#ifdef CONFIG_AX25
memcpy(dev->broadcast,ax25_bcast,7); /* Only activated in AX.25 mode */
memcpy(dev->dev_addr,ax25_test,7); /* "" "" "" "" */
#endif
dev->queue_xmit = dev_queue_xmit;
dev->rebuild_header = sl_rebuild_header;
for (i = 0; i < DEV_NUMBUFFS; i++)
dev->buffs[i] = NULL;
/* New-style flags. */
dev->flags = 0;
dev->family = AF_INET;
dev->pa_addr = 0;
dev->pa_brdaddr = 0;
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
return(0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -