📄 slip.c
字号:
/* Add an ARP-entry for this device's broadcast address. Not used. */static voidsl_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 intsl_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 intsl_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 intsl_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 voidslip_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 intslip_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 voidslip_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_AX25int 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 intslip_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. */intslip_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 + -