1197 lines
27 KiB
C
1197 lines
27 KiB
C
|
/*
|
||
|
* slip.c This module implements the SLIP protocol for kernel-based
|
||
|
* devices like TTY. It interfaces between a raw TTY, and the
|
||
|
* kernel's INET protocol layers (via DDI).
|
||
|
*
|
||
|
* Version: @(#)slip.c 0.7.6 05/25/93
|
||
|
*
|
||
|
* Authors: Laurence Culhane, <loz@holmes.demon.co.uk>
|
||
|
* Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
|
||
|
*
|
||
|
* Fixes:
|
||
|
* Alan Cox : Sanity checks and avoid tx overruns.
|
||
|
* Has a new sl->mtu field.
|
||
|
* Alan Cox : Found cause of overrun. ifconfig sl0 mtu upwards.
|
||
|
* Driver now spots this and grows/shrinks its buffers(hack!).
|
||
|
* Memory leak if you run out of memory setting up a slip driver fixed.
|
||
|
* Matt Dillon : Printable slip (borrowed from NET2E)
|
||
|
* Pauline Middelink : Slip driver fixes.
|
||
|
* Alan Cox : Honours the old SL_COMPRESSED flag
|
||
|
* Alan Cox : KISS AX.25 and AXUI IP support
|
||
|
* Michael Riepe : Automatic CSLIP recognition added
|
||
|
*/
|
||
|
|
||
|
#include <asm/segment.h>
|
||
|
#include <asm/system.h>
|
||
|
|
||
|
#include <linux/config.h>
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/string.h>
|
||
|
#include <linux/mm.h>
|
||
|
#include <linux/socket.h>
|
||
|
#include <linux/sockios.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/tty.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/stat.h>
|
||
|
#include <linux/in.h>
|
||
|
#include "inet.h"
|
||
|
#include "dev.h"
|
||
|
#ifdef CONFIG_AX25
|
||
|
#include "ax25.h"
|
||
|
#endif
|
||
|
#include "eth.h"
|
||
|
#include "ip.h"
|
||
|
#include "route.h"
|
||
|
#include "protocol.h"
|
||
|
#include "tcp.h"
|
||
|
#include "skbuff.h"
|
||
|
#include "sock.h"
|
||
|
#include "arp.h"
|
||
|
#include "slip.h"
|
||
|
#include "slhc.h"
|
||
|
|
||
|
#define SLIP_VERSION "0.7.5"
|
||
|
|
||
|
/* Define some IP layer stuff. Not all systems have it. */
|
||
|
#ifdef SL_DUMP
|
||
|
# define IP_VERSION 4 /* version# of our IP software */
|
||
|
# define IPF_F_OFFSET 0x1fff /* Offset field */
|
||
|
# define IPF_DF 0x4000 /* Don't fragment flag */
|
||
|
# define IPF_MF 0x2000 /* More Fragments flag */
|
||
|
# define IP_OF_COPIED 0x80 /* Copied-on-fragmentation flag */
|
||
|
# define IP_OF_CLASS 0x60 /* Option class */
|
||
|
# define IP_OF_NUMBER 0x1f /* Option number */
|
||
|
#endif
|
||
|
|
||
|
|
||
|
static struct slip sl_ctrl[SL_NRUNIT];
|
||
|
static struct tty_ldisc sl_ldisc;
|
||
|
static int already = 0;
|
||
|
|
||
|
|
||
|
/* Dump the contents of an IP datagram. */
|
||
|
static void
|
||
|
ip_dump(unsigned char *ptr, int len)
|
||
|
{
|
||
|
#ifdef SL_DUMP
|
||
|
struct iphdr *ip;
|
||
|
struct tcphdr *th;
|
||
|
int dlen, doff;
|
||
|
|
||
|
if (inet_debug != DBG_SLIP) return;
|
||
|
|
||
|
ip = (struct iphdr *) ptr;
|
||
|
th = (struct tcphdr *) (ptr + ip->ihl * 4);
|
||
|
printk("\r%s -> %s seq %lx ack %lx len %d\n",
|
||
|
in_ntoa(ip->saddr), in_ntoa(ip->daddr),
|
||
|
ntohl(th->seq), ntohl(th->ack_seq), ntohs(ip->tot_len));
|
||
|
return;
|
||
|
|
||
|
printk("\r*****\n");
|
||
|
printk("%p %d\n", ptr, len);
|
||
|
ip = (struct iphdr *) ptr;
|
||
|
dlen = ntohs(ip->tot_len);
|
||
|
doff = ((ntohs(ip->frag_off) & IPF_F_OFFSET) << 3);
|
||
|
|
||
|
|
||
|
printk("SLIP: %s->", in_ntoa(ip->saddr));
|
||
|
printk("%s\n", in_ntoa(ip->daddr));
|
||
|
printk(" len %u ihl %u ver %u ttl %u prot %u",
|
||
|
dlen, ip->ihl, ip->version, ip->ttl, ip->protocol);
|
||
|
|
||
|
if (ip->tos != 0) printk(" tos %u", ip->tos);
|
||
|
if (doff != 0 || (ntohs(ip->frag_off) & IPF_MF))
|
||
|
printk(" id %u offs %u", ntohs(ip->id), doff);
|
||
|
|
||
|
if (ntohs(ip->frag_off) & IPF_DF) printk(" DF");
|
||
|
if (ntohs(ip->frag_off) & IPF_MF) printk(" MF");
|
||
|
printk("\n*****\n");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
void clh_dump(unsigned char *cp, int len)
|
||
|
{
|
||
|
if (len > 60)
|
||
|
len = 60;
|
||
|
printk("%d:", len);
|
||
|
while (len > 0) {
|
||
|
printk(" %x", *cp++);
|
||
|
len--;
|
||
|
}
|
||
|
printk("\n\n");
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Initialize a SLIP control block for use. */
|
||
|
static void
|
||
|
sl_initialize(struct slip *sl, struct device *dev)
|
||
|
{
|
||
|
sl->inuse = 0;
|
||
|
sl->sending = 0;
|
||
|
sl->escape = 0;
|
||
|
sl->flags = 0;
|
||
|
#ifdef SL_ADAPTIVE
|
||
|
sl->mode = SL_MODE_ADAPTIVE; /* automatic CSLIP recognition */
|
||
|
#else
|
||
|
#ifdef SL_COMPRESSED
|
||
|
sl->mode = SL_MODE_CSLIP | SL_MODE_ADAPTIVE; /* Default */
|
||
|
#else
|
||
|
sl->mode = SL_MODE_SLIP; /* Default for non compressors */
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
sl->line = dev->base_addr;
|
||
|
sl->tty = NULL;
|
||
|
sl->dev = dev;
|
||
|
sl->slcomp = NULL;
|
||
|
|
||
|
/* Clear all pointers. */
|
||
|
sl->rbuff = NULL;
|
||
|
sl->xbuff = NULL;
|
||
|
sl->cbuff = NULL;
|
||
|
|
||
|
sl->rhead = NULL;
|
||
|
sl->rend = NULL;
|
||
|
dev->rmem_end = (unsigned long) NULL;
|
||
|
dev->rmem_start = (unsigned long) NULL;
|
||
|
dev->mem_end = (unsigned long) NULL;
|
||
|
dev->mem_start = (unsigned long) NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Find a SLIP channel from its `tty' link. */
|
||
|
static struct slip *
|
||
|
sl_find(struct tty_struct *tty)
|
||
|
{
|
||
|
struct slip *sl;
|
||
|
int i;
|
||
|
|
||
|
if (tty == NULL) return(NULL);
|
||
|
for (i = 0; i < SL_NRUNIT; i++) {
|
||
|
sl = &sl_ctrl[i];
|
||
|
if (sl->tty == tty) return(sl);
|
||
|
}
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Find a free SLIP channel, and link in this `tty' line. */
|
||
|
static inline struct slip *
|
||
|
sl_alloc(void)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
struct slip *sl;
|
||
|
int i;
|
||
|
|
||
|
save_flags (flags);
|
||
|
cli();
|
||
|
for (i = 0; i < SL_NRUNIT; i++) {
|
||
|
sl = &sl_ctrl[i];
|
||
|
if (sl->inuse == 0) {
|
||
|
sl->inuse = 1;
|
||
|
sl->tty = NULL;
|
||
|
restore_flags(flags);
|
||
|
return(sl);
|
||
|
}
|
||
|
}
|
||
|
restore_flags(flags);
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Free a SLIP channel. */
|
||
|
static inline void
|
||
|
sl_free(struct slip *sl)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
if (sl->inuse) {
|
||
|
save_flags(flags);
|
||
|
cli();
|
||
|
sl->inuse = 0;
|
||
|
sl->tty = NULL;
|
||
|
restore_flags(flags);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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 sl_changedmtu(struct slip *sl)
|
||
|
{
|
||
|
struct device *dev=sl->dev;
|
||
|
unsigned char *tb,*rb,*cb,*tf,*rf,*cf;
|
||
|
int l;
|
||
|
int omtu=sl->mtu;
|
||
|
|
||
|
sl->mtu=dev->mtu;
|
||
|
l=(dev->mtu *2);
|
||
|
|
||
|
DPRINTF((DBG_SLIP,"SLIP: mtu changed!\n"));
|
||
|
|
||
|
tb= (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
|
||
|
rb= (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
|
||
|
cb= (unsigned char *) kmalloc(l + 4, GFP_KERNEL);
|
||
|
|
||
|
if(tb==NULL || rb==NULL || cb==NULL)
|
||
|
{
|
||
|
printk("Unable to grow slip buffers. MTU change cancelled.\n");
|
||
|
sl->mtu=omtu;
|
||
|
dev->mtu=omtu;
|
||
|
if(tb!=NULL)
|
||
|
kfree(tb);
|
||
|
if(rb!=NULL)
|
||
|
kfree(rb);
|
||
|
if(cb!=NULL)
|
||
|
kfree(cb);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
cli();
|
||
|
|
||
|
tf=(unsigned char *)sl->dev->mem_start;
|
||
|
sl->dev->mem_start=(unsigned long)tb;
|
||
|
sl->dev->mem_end=(unsigned long) (sl->dev->mem_start + l);
|
||
|
rf=(unsigned char *)sl->dev->rmem_start;
|
||
|
sl->dev->rmem_start=(unsigned long)rb;
|
||
|
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;
|
||
|
|
||
|
cf=sl->cbuff;
|
||
|
sl->cbuff=cb;
|
||
|
|
||
|
sl->escape=0;
|
||
|
sl->sending=0;
|
||
|
sl->rcount=0;
|
||
|
|
||
|
sti();
|
||
|
|
||
|
if(rf!=NULL)
|
||
|
kfree(rf);
|
||
|
if(tf!=NULL)
|
||
|
kfree(tf);
|
||
|
if(cf!=NULL)
|
||
|
kfree(cf);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Stuff one byte into a SLIP receiver buffer. */
|
||
|
static inline void
|
||
|
sl_enqueue(struct slip *sl, unsigned char c)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
save_flags(flags);
|
||
|
cli();
|
||
|
if (sl->rhead < sl->rend) {
|
||
|
*sl->rhead = c;
|
||
|
sl->rhead++;
|
||
|
sl->rcount++;
|
||
|
} else sl->roverrun++;
|
||
|
restore_flags(flags);
|
||
|
}
|
||
|
|
||
|
/* Release 'i' bytes from a SLIP receiver buffer. */
|
||
|
static inline void
|
||
|
sl_dequeue(struct slip *sl, int i)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
save_flags(flags);
|
||
|
cli();
|
||
|
if (sl->rhead > sl->rbuff) {
|
||
|
sl->rhead -= i;
|
||
|
sl->rcount -= i;
|
||
|
}
|
||
|
restore_flags(flags);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Set the "sending" flag. This must be atomic, hence the ASM. */
|
||
|
static inline void
|
||
|
sl_lock(struct slip *sl)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
save_flags(flags);
|
||
|
cli();
|
||
|
sl->sending = 1;
|
||
|
sl->dev->tbusy = 1;
|
||
|
restore_flags(flags);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Clear the "sending" flag. This must be atomic, hence the ASM. */
|
||
|
static inline void
|
||
|
sl_unlock(struct slip *sl)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
|
||
|
save_flags(flags);
|
||
|
cli();
|
||
|
sl->sending = 0;
|
||
|
sl->dev->tbusy = 0;
|
||
|
restore_flags(flags);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Send one completely decapsulated IP datagram to the IP layer. */
|
||
|
static void
|
||
|
sl_bump(struct slip *sl)
|
||
|
{
|
||
|
int done;
|
||
|
unsigned char c;
|
||
|
unsigned long flags;
|
||
|
int count;
|
||
|
|
||
|
count = sl->rcount;
|
||
|
if (sl->mode & (SL_MODE_ADAPTIVE | SL_MODE_CSLIP)) {
|
||
|
if ((c = sl->rbuff[0]) & SL_TYPE_COMPRESSED_TCP) {
|
||
|
#if 1
|
||
|
/* ignore compressed packets when CSLIP is off */
|
||
|
if (!(sl->mode & SL_MODE_CSLIP)) {
|
||
|
printk("SLIP: compressed packet ignored\n");
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
/* make sure we've reserved enough space for uncompress to use */
|
||
|
save_flags(flags);
|
||
|
cli();
|
||
|
if ((sl->rhead + 80) < sl->rend) {
|
||
|
sl->rhead += 80;
|
||
|
sl->rcount += 80;
|
||
|
done = 1;
|
||
|
} else {
|
||
|
sl->roverrun++;
|
||
|
done = 0;
|
||
|
}
|
||
|
restore_flags(flags);
|
||
|
if (! done) /* not enough space available */
|
||
|
return;
|
||
|
|
||
|
count = slhc_uncompress(sl->slcomp, sl->rbuff, count);
|
||
|
if (count <= 0) {
|
||
|
sl->errors++;
|
||
|
return;
|
||
|
}
|
||
|
} else if (c >= SL_TYPE_UNCOMPRESSED_TCP) {
|
||
|
if (!(sl->mode & SL_MODE_CSLIP)) {
|
||
|
/* turn on header compression */
|
||
|
sl->mode |= SL_MODE_CSLIP;
|
||
|
printk("SLIP: header compression turned on\n");
|
||
|
}
|
||
|
sl->rbuff[0] &= 0x4f;
|
||
|
if (slhc_remember(sl->slcomp, sl->rbuff, count) <= 0) {
|
||
|
sl->errors++;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DPRINTF((DBG_SLIP, "<< \"%s\" recv:\r\n", sl->dev->name));
|
||
|
ip_dump(sl->rbuff, sl->rcount);
|
||
|
|
||
|
/* Bump the datagram to the upper layers... */
|
||
|
do {
|
||
|
DPRINTF((DBG_SLIP, "SLIP: packet is %d at 0x%X\n",
|
||
|
sl->rcount, sl->rbuff));
|
||
|
/* clh_dump(sl->rbuff, count); */
|
||
|
done = dev_rint(sl->rbuff, count, 0, sl->dev);
|
||
|
if (done == 0 || done == 1) break;
|
||
|
} while(1);
|
||
|
|
||
|
sl->rpacket++;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* TTY finished sending a datagram, so clean up. */
|
||
|
static void
|
||
|
sl_next(struct slip *sl)
|
||
|
{
|
||
|
DPRINTF((DBG_SLIP, "SLIP: sl_next(0x%X) called!\n", sl));
|
||
|
sl_unlock(sl);
|
||
|
dev_tint(sl->dev);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Encapsulate one IP datagram and stuff into a TTY queue. */
|
||
|
static void
|
||
|
sl_encaps(struct slip *sl, unsigned char *icp, int len)
|
||
|
{
|
||
|
unsigned char *bp, *p;
|
||
|
int count;
|
||
|
|
||
|
DPRINTF((DBG_SLIP, "SLIP: sl_encaps(0x%X, %d) called\n", icp, len));
|
||
|
DPRINTF((DBG_SLIP, ">> \"%s\" sent:\r\n", sl->dev->name));
|
||
|
|
||
|
ip_dump(icp, len);
|
||
|
|
||
|
if(sl->mtu != sl->dev->mtu) /* Someone has been ifconfigging */
|
||
|
sl_changedmtu(sl);
|
||
|
|
||
|
if(len>sl->mtu) /* Sigh, shouldn't occur BUT ... */
|
||
|
{
|
||
|
len=sl->mtu;
|
||
|
printk("slip: truncating oversized transmit packet!\n");
|
||
|
}
|
||
|
|
||
|
p = icp;
|
||
|
if(sl->mode & SL_MODE_CSLIP)
|
||
|
len = slhc_compress(sl->slcomp, p, len, sl->cbuff, &p, 1);
|
||
|
|
||
|
#ifdef OLD
|
||
|
/*
|
||
|
* Send an initial END character to flush out any
|
||
|
* data that may have accumulated in the receiver
|
||
|
* due to line noise.
|
||
|
*/
|
||
|
bp = sl->xbuff;
|
||
|
*bp++ = END;
|
||
|
count = 1;
|
||
|
|
||
|
/*
|
||
|
* For each byte in the packet, send the appropriate
|
||
|
* character sequence, according to the SLIP protocol.
|
||
|
*/
|
||
|
while(len-- > 0) {
|
||
|
c = *p++;
|
||
|
switch(c) {
|
||
|
case END:
|
||
|
*bp++ = ESC;
|
||
|
*bp++ = ESC_END;
|
||
|
count += 2;
|
||
|
break;
|
||
|
case ESC:
|
||
|
*bp++ = ESC;
|
||
|
*bp++ = ESC_ESC;
|
||
|
count += 2;
|
||
|
break;
|
||
|
default:
|
||
|
*bp++ = c;
|
||
|
count++;
|
||
|
}
|
||
|
}
|
||
|
*bp++ = END;
|
||
|
count++;
|
||
|
#else
|
||
|
if(sl->mode & SL_MODE_SLIP6)
|
||
|
count=slip_esc6(p, (unsigned char *)sl->xbuff,len);
|
||
|
else
|
||
|
count=slip_esc(p, (unsigned char *)sl->xbuff,len);
|
||
|
#endif
|
||
|
sl->spacket++;
|
||
|
bp = sl->xbuff;
|
||
|
|
||
|
/* Tell TTY to send it on its way. */
|
||
|
DPRINTF((DBG_SLIP, "SLIP: kicking TTY for %d bytes at 0x%X\n", count, bp));
|
||
|
if (tty_write_data(sl->tty, (char *) bp, count,
|
||
|
(void (*)(void *))sl_next, (void *) sl) == 0) {
|
||
|
DPRINTF((DBG_SLIP, "SLIP: TTY already done with %d bytes!\n", count));
|
||
|
sl_next(sl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*static void sl_hex_dump(unsigned char *x,int l)
|
||
|
{
|
||
|
int n=0;
|
||
|
printk("sl_xmit: (%d bytes)\n",l);
|
||
|
while(l)
|
||
|
{
|
||
|
printk("%2X ",(int)*x++);
|
||
|
l--;
|
||
|
n++;
|
||
|
if(n%32==0)
|
||
|
printk("\n");
|
||
|
}
|
||
|
if(n%32)
|
||
|
printk("\n");
|
||
|
}*/
|
||
|
|
||
|
/* Encapsulate an IP datagram and kick it into a TTY queue. */
|
||
|
static int
|
||
|
sl_xmit(struct sk_buff *skb, struct device *dev)
|
||
|
{
|
||
|
struct tty_struct *tty;
|
||
|
struct slip *sl;
|
||
|
|
||
|
/* Find the correct SLIP channel to use. */
|
||
|
sl = &sl_ctrl[dev->base_addr];
|
||
|
tty = sl->tty;
|
||
|
DPRINTF((DBG_SLIP, "SLIP: sl_xmit(\"%s\") skb=0x%X busy=%d\n",
|
||
|
dev->name, skb, sl->sending));
|
||
|
|
||
|
/*
|
||
|
* 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...
|
||
|
*/
|
||
|
if (sl->sending) {
|
||
|
DPRINTF((DBG_SLIP, "SLIP: sl_xmit: BUSY\r\n"));
|
||
|
sl->sbusy++;
|
||
|
return(1);
|
||
|
}
|
||
|
|
||
|
/* We were not, so we are now... :-) */
|
||
|
if (skb != NULL) {
|
||
|
#ifdef CONFIG_AX25
|
||
|
if(sl->mode & SL_MODE_AX25)
|
||
|
{
|
||
|
if(!skb->arp && dev->rebuild_header(skb->data,dev))
|
||
|
{
|
||
|
skb->dev=dev;
|
||
|
arp_queue(skb);
|
||
|
return 0;
|
||
|
}
|
||
|
skb->arp=1;
|
||
|
}
|
||
|
#endif
|
||
|
sl_lock(sl);
|
||
|
/* sl_hex_dump(skb->data,skb->len);*/
|
||
|
sl_encaps(sl, skb->data, skb->len);
|
||
|
if (skb->free) kfree_skb(skb, FREE_WRITE);
|
||
|
}
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Return the frame type ID. This is normally IP but maybe be AX.25. */
|
||
|
static unsigned short
|
||
|
sl_type_trans (struct sk_buff *skb, struct device *dev)
|
||
|
{
|
||
|
#ifdef CONFIG_AX25
|
||
|
struct slip *sl=&sl_ctrl[dev->base_addr];
|
||
|
if(sl->mode&SL_MODE_AX25)
|
||
|
return(NET16(ETH_P_AX25));
|
||
|
#endif
|
||
|
return(NET16(ETH_P_IP));
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Fill in the MAC-level header. Not used by SLIP. */
|
||
|
static int
|
||
|
sl_header(unsigned char *buff, struct device *dev, unsigned short type,
|
||
|
unsigned long daddr, unsigned long saddr, unsigned len)
|
||
|
{
|
||
|
#ifdef CONFIG_AX25
|
||
|
struct slip *sl=&sl_ctrl[dev->base_addr];
|
||
|
if((sl->mode&SL_MODE_AX25) && type!=NET16(ETH_P_AX25))
|
||
|
return ax25_encapsulate_ip(buff,dev,type,daddr,saddr,len);
|
||
|
#endif
|
||
|
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
|
||
|
/* 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);
|
||
|
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);
|
||
|
}
|