/* * 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, * Fred N. van Kempen, * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }