?? plip.c
字號:
plip_receive_packet(struct device *dev){ int plip_type; unsigned length; int checksum = 0; struct sk_buff *skb; struct netstats *localstats; struct ethhdr eth; localstats = (struct netstats*) dev->priv; outb(1, dev->base_addr + PAR_DATA); /* Ack: 'Ready' */ { /* get header octet and length of packet */ plip_type = get_byte(dev); if (plip_type < 0) return 1; /* probably wrong interrupt */ length = get_byte(dev) << 8; length |= get_byte(dev); switch ( plip_type ) { case PLIP_HEADER_TYPE1: { int i; unsigned char *eth_p = (unsigned char*)ð for ( i = 0; i < sizeof(eth); i++, eth_p++) { *eth_p = get_byte(dev); } } break; case PLIP_HEADER_TYPE2: { unsigned char h_dest, h_source; unsigned short type; h_dest = get_byte(dev); h_source = get_byte(dev); type = get_byte(dev) << 8; type |= get_byte(dev); plip_rebuild_enethdr(dev, ð, h_dest, h_source, type); } break; default: PRINTK(("%s: wrong header octet\n", dev->name)); } PRINTK2(("length = %d\n", length)); if (length > dev->mtu || length < 8) { PRINTK2(("%s: bogus packet size %d.\n", dev->name, length)); return 1; } } { /* get skb area from kernel and * set appropriate values to skb */ int sksize; sksize = sizeof(struct sk_buff) + length; skb = alloc_skb(sksize, GFP_ATOMIC); if (skb == NULL) { PRINTK(("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, sksize)); return 1; } skb->lock = 0; skb->mem_len = sksize; skb->mem_addr = skb; } { /* phase of receiving the data */ /* 'skb->data' points to the start of sk_buff data area. */ unsigned char *buf = skb->data; unsigned char *eth_p = (unsigned char *)ð int i; for ( i = 0; i < sizeof(eth); i++) { checksum += *eth_p; *buf++ = *eth_p++; } for ( i = 0; i < length - sizeof(eth); i++) { unsigned char new_byte = get_byte(dev); checksum += new_byte; *buf++ = new_byte; } checksum &= 0xff; if (checksum != get_byte(dev)) { localstats->rx_crc_errors++; PRINTK(("checksum error\n")); return 1; } else if(dev_rint((unsigned char *)skb, length, IN_SKBUFF, dev)) { printk("%s: rcv buff full.\n", dev->name); localstats->rx_dropped++; return 1; } } { /* phase of terminating this connection */ int timeout; timeout = jiffies + length * timeoutfactor / 16; outb(0x00, dev->base_addr + PAR_DATA); /* Wait for the remote end to reset. */ while ( (inb(dev->base_addr + PAR_STATUS) & 0xf8) != 0x80 ) { if (timeout < jiffies ) { double_timeoutfactor(); PRINTK(("Remote has not reset.\n")); break; } } } localstats->rx_packets++; return 0;}static int send_byte(struct device *dev, unsigned char val){ int timeout; int error = 0; if (!(inb(dev->base_addr+PAR_STATUS) & 0x08)) { PRINTK(("remote end become unready while sending\n")); return -1; } PRINTK2((" S%02x", val)); outb(val, dev->base_addr); /* this makes data bits more stable */ outb(0x10 | val, dev->base_addr); timeout = jiffies + timeoutfactor; while( inb(dev->base_addr+PAR_STATUS) & 0x80 ) if ( timeout < jiffies ) { error++; break; } outb(0x10 | (val >> 4), dev->base_addr); outb(val >> 4, dev->base_addr); timeout = jiffies + timeoutfactor; while( (inb(dev->base_addr+PAR_STATUS) & 0x80) == 0 ) if ( timeout < jiffies ) { error++; break; } if (error) { /* timeout error */ double_timeoutfactor(); PRINTK2(("t")); return -1; } return 0;}/* * plip_send_start * trigger remoto rx interrupt and establish a connection. * * return value * 0 : establish the connection * -1 : connection failed. */static intplip_send_start(struct device *dev, struct ethhdr *eth){ int timeout; int status; int lasttrigger; struct netstats *localstats = (struct netstats*) dev->priv; /* This starts the packet protocol by triggering a remote IRQ. */ timeout = jiffies + timeoutfactor * 16; lasttrigger = jiffies; while ( ((status = inb(dev->base_addr+PAR_STATUS)) & 0x08) == 0 ) { dev->tbusy = 1; outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable my rx intr. */ outb(0x08, dev->base_addr + PAR_DATA); /* Trigger remote rx intr. */ if (status & 0x40) { /* The remote end is also trying to send a packet. * Only one end may go to the receiving phase, * so we use the "ethernet" address (set from the IP address) * to determine which end dominates. */ if ( plip_addrcmp(eth) > 0 ) { localstats->collisions++; PRINTK2(("both ends are trying to send a packet.\n")); if (plip_receive_packet(dev)) { /* get some error while receiving data */ localstats->rx_errors++; outb(0x02, dev->base_addr + PAR_DATA); } else { outb(0x00, dev->base_addr + PAR_DATA); } cold_sleep(2); /* make sure that remote end is ready */ } continue; /* restart send sequence */ } if (lasttrigger != jiffies) { /* trigger again */ outb(0x00, dev->base_addr + PAR_DATA); cold_sleep(1); lasttrigger = jiffies; } if (timeout < jiffies) { double_timeoutfactor(); plip_device_clear(dev); localstats->tx_errors++; PRINTK(("%s: Connect failed in send_packet().\n", dev->name)); /* We failed to send the packet. To emulate the ethernet we should pretent the send worked fine */ return -1; } } return 0;}static intplip_send_packet(struct device *dev, unsigned char *buf, int length){ int error = 0; int plip_type; struct netstats *localstats; PRINTK2(("%s: plip_send_packet(%d) %02x %02x %02x %02x %02x...", dev->name, length, buf[0], buf[1], buf[2], buf[3], buf[4])); if (length > dev->mtu) { printk("%s: packet too big, %d.\n", dev->name, length); return 0; } localstats = (struct netstats*) dev->priv; { /* phase of checking remote status */ int i; int timeout = jiffies + timeoutfactor * 8; while ( (i = (inb(dev->base_addr+PAR_STATUS) & 0xe8)) != 0x80 ) { if (i == 0x78) { /* probably cable is not connected */ /* Implementation Note: * This status should result in 'Network unreachable'. * but I don't know the way. */ return 0; } if (timeout < jiffies) { /* remote end is not ready */ double_timeoutfactor(); localstats->tx_errors++; PRINTK(("remote end is not ready.\n")); return 1; /* Failed to send the packet */ } } } /* phase of making a connection */ if (plip_send_start(dev, (struct ethhdr *)buf) < 0) return 1; /* select plip type */ { /* Use stripped ethernet header if each first 5 octet of eth * address is same. */ int i; struct ethhdr *eth = (struct ethhdr *)buf; plip_type = PLIP_HEADER_TYPE2; for ( i = 0; i < ETH_ALEN - 1; i++) if (eth->h_dest[i] != eth->h_source[i]) plip_type = PLIP_HEADER_TYPE1; } send_byte(dev, plip_type); /* send header octet */ { /* send packet's length */ /* * in original plip (before v0.1), it was sent with little endian. * but in internet, network byteorder is big endian, * so changed to use big endian. * maybe using 'ntos()' is better. */ send_byte(dev, length >> 8); send_byte(dev, length); } { /* phase of sending data */ int i; int checksum = 0; if (plip_type == PLIP_HEADER_TYPE2) { plip_send_enethdr(dev, (struct ethhdr*)buf); } for ( i = 0; i < sizeof(struct ethhdr); i++ ) { if (plip_type == PLIP_HEADER_TYPE1) { send_byte(dev, *buf); } checksum += *buf++; } for (i = 0; i < length - sizeof(struct ethhdr); i++) { checksum += buf[i]; if (send_byte(dev, buf[i]) < 0) { error++; break; } } send_byte(dev, checksum & 0xff); } { /* phase of terminating this connection */ int timeout; outb(0x00, dev->base_addr + PAR_DATA); /* Wait for the remote end to reset. */ timeout = jiffies + ((length * timeoutfactor) >> 4); while ((inb(dev->base_addr + PAR_STATUS) & 0xe8) != 0x80) { if (timeout < jiffies ) { double_timeoutfactor(); PRINTK(("Remote end has not reset.\n")); error++; break; } } if (inb(dev->base_addr + PAR_STATUS) & 0x10) { /* receiver reports error */ error++; } } plip_device_clear(dev); localstats->tx_packets++; PRINTK2(("plip_send_packet(%d) done.\n", length)); return error?1:0;}/* * some trivial functions */static voidplip_set_physicaladdr(struct device *dev, unsigned long ipaddr){ /* * set physical address to * 0xfd.0xfd.ipaddr */ unsigned char *addr = dev->dev_addr; int i; if ((ipaddr >> 24) == 0 || (ipaddr >> 24) == 0xff) return; PRINTK2(("%s: set physical address to %08x\n", dev->name, ipaddr)); for (i=0; i < ETH_ALEN - sizeof(unsigned long); i++) { addr[i] = 0xfd; } memcpy(&(addr[i]), &ipaddr, sizeof(unsigned long));}static intplip_addrcmp(struct ethhdr *eth){ int i; for ( i = ETH_ALEN - 1; i >= 0; i-- ) { if (eth->h_dest[i] > eth->h_source[i]) return -1; if (eth->h_dest[i] < eth->h_source[i]) return 1; } PRINTK2(("h_dest = %08x%04x h_source = %08x%04x\n", *(long*)ð->h_dest[2],*(short*)ð->h_dest[0], *(long*)ð->h_source[2],*(short*)ð->h_source[0])); return 0;}static intplip_send_enethdr(struct device *dev, struct ethhdr *eth){ send_byte(dev, eth->h_dest[ETH_ALEN-1]); send_byte(dev, eth->h_source[ETH_ALEN-1]); send_byte(dev, eth->h_proto >> 8); send_byte(dev, eth->h_proto); return 0;}static intplip_rebuild_enethdr(struct device *dev, struct ethhdr *eth, unsigned char dest, unsigned char source, unsigned short type){ eth->h_proto = type; memcpy(eth->h_dest, dev->dev_addr, ETH_ALEN-1); eth->h_dest[ETH_ALEN-1] = dest; memcpy(eth->h_source, dev->dev_addr, ETH_ALEN-1); eth->h_source[ETH_ALEN-1] = source; return 0;}/* This function is evil, evil, evil. This should be a _kernel_, rescheduling sleep!. */static voidcold_sleep(int tics){ int start = jiffies; while(jiffies < start + tics) ; /* do nothing */ return;}static void double_timeoutfactor(){ timeoutfactor *= 2; if (timeoutfactor >= MAXTIMEOUTFACTOR) { timeoutfactor = MAXTIMEOUTFACTOR; } return;}static struct enet_statistics *plip_get_stats(struct device *dev){ struct netstats *localstats = (struct netstats*) dev->priv; return localstats;}/* * Local variables: * compile-command: "gcc -D__KERNEL__ -Wall -O6 -fomit-frame-pointer -x c++ -c plip.c" * version-control: t * kept-new-versions: 5 * End: */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -