⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 plip.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
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*)&eth;
		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, &eth, 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 *)&eth;
	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 int
plip_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 int
plip_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 void
plip_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 int
plip_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*)&eth->h_dest[2],*(short*)&eth->h_dest[0],
	    *(long*)&eth->h_source[2],*(short*)&eth->h_source[0]));
    return 0;
}

static int
plip_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 int
plip_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 void
cold_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 + -