| /* Plip.c: A parallel port "network" driver for linux. */ |
| /* |
| Written 1993 by Donald Becker and TANABE Hiroyasu. |
| This code is distributed under the GPL. |
| |
| The current author is reached as hiro@sanpo.t.u-tokyo.ac.jp . |
| For more information do 'whois -h whois.nic.ad.jp HT043JP' |
| |
| The original author may be reached as becker@super.org or |
| C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715 |
| |
| This is parallel port packet pusher. It's actually more general |
| than the "IP" in its name suggests -- but 'plip' is just such a |
| great name! |
| |
| This driver was first developed by D. Becker, when he was inspired by |
| Russ Nelson's parallel port packet driver. He also did the update |
| to 0.99.10. |
| |
| It was further developed by Tommy Thorn (tthorn@daimi.aau.dk). |
| |
| Recent versions were debugged and maintained by TANABE Hiroyasu. |
| |
| Updated for 0.99pl12 by Donald Becker. |
| |
| Changes even more Alan Cox <iiitac@pyr.swan.ac.uk> |
| Fixed: sets skb->arp=1, always claims success like ethernet, doesn't |
| free skb and then claim fail. Incorrect brackets causing reset problem |
| Attempting to make it work (works for me - email me if it does work) |
| |
| Bugs: |
| Should be timer oriented state machine. |
| Should never use jiffies for timeouts. |
| Protocol is buggy when broadcasts occur (Must ask Russ Nelson) |
| Can hang forever on collisions (tough - you fix it!). |
| I get 15K/second NFS throughput (about 20-25K second IP). |
| Change the protocol back. |
| |
| */ |
| |
| static char *version = |
| "Net2Debugged PLIP 1.01 (from plip.c:v0.15 for 0.99pl12+, 8/11/93)\n"; |
| |
| #include <linux/config.h> |
| |
| /* |
| Sources: |
| Ideas and protocols came from Russ Nelson's (nelson@crynwr.com) |
| "parallel.asm" parallel port packet driver. |
| TANABE Hiroyasu changes the protocol. |
| The "Crynwr" parallel port standard specifies the following protocol: |
| send header nibble '8' |
| type octet '0xfd' or '0xfc' |
| count-low octet |
| count-high octet |
| ... data octets |
| checksum octet |
| Each octet is sent as <wait for rx. '0x1?'> <send 0x10+(octet&0x0F)> |
| <wait for rx. '0x0?'> <send 0x00+((octet>>4)&0x0F)> |
| |
| The cable used is a de facto standard parallel null cable -- sold as |
| a "LapLink" cable by various places. You'll need a 10-conductor cable to |
| make one yourself. The wiring is: |
| INIT 16 - 16 SLCTIN 17 - 17 |
| GROUND 25 - 25 |
| D0->ERROR 2 - 15 15 - 2 |
| D1->SLCT 3 - 13 13 - 3 |
| D2->PAPOUT 4 - 12 12 - 4 |
| D3->ACK 5 - 10 10 - 5 |
| D4->BUSY 6 - 11 11 - 6 |
| Do not connect the other pins. They are |
| D5,D6,D7 are 7,8,9 |
| STROBE is 1, FEED is 14 |
| extra grounds are 18,19,20,21,22,23,24 |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/sched.h> |
| #include <linux/types.h> |
| #include <linux/fcntl.h> |
| #include <linux/interrupt.h> |
| #include <linux/string.h> |
| #include <linux/ptrace.h> |
| #include <linux/if_ether.h> |
| #include <asm/system.h> |
| #include <asm/io.h> |
| #include <netinet/in.h> |
| #include <errno.h> |
| |
| #include "dev.h" |
| #include "eth.h" |
| #include "ip.h" |
| #include "protocol.h" |
| #include "tcp.h" |
| #include "skbuff.h" |
| #include "sock.h" |
| #include "arp.h" |
| |
| #ifdef PRINTK |
| #undef PRINTK |
| #endif |
| #ifdef PRINTK2 |
| #undef PRINTK2 |
| #endif |
| |
| #define PLIP_DEBUG /* debugging */ |
| #undef PLIP_DEBUG2 /* debugging with more varbose report */ |
| |
| #ifdef PLIP_DEBUG |
| #define PRINTK(x) printk x |
| #else |
| #define PRINTK(x) /**/ |
| #endif |
| #ifdef PLIP_DEBUG2 |
| #define PRINTK2(x) printk x |
| #else |
| #define PRINTK2(x) /**/ |
| #endif |
| |
| /* The map from IRQ number (as passed to the interrupt handler) to |
| 'struct device'. */ |
| extern struct device *irq2dev_map[16]; |
| |
| /* Network statistics, with the same names as 'struct enet_statistics'. */ |
| #define netstats enet_statistics |
| |
| /* constants */ |
| #define PAR_DATA 0 |
| #define PAR_STATUS 1 |
| #define PAR_CONTROL 2 |
| #define PLIP_MTU 1600 |
| #define PLIP_HEADER_TYPE1 0xfd |
| #define PLIP_HEADER_TYPE2 0xfc |
| |
| /* Index to functions, as function prototypes. */ |
| extern int plip_probe(int ioaddr, struct device *dev); |
| static int plip_open(struct device *dev); |
| static int plip_close(struct device *dev); |
| static int plip_tx_packet(struct sk_buff *skb, struct device *dev); |
| static int plip_header (unsigned char *buff, struct device *dev, |
| unsigned short type, unsigned long h_dest, |
| unsigned long h_source, unsigned len); |
| |
| /* variables used internally. */ |
| #define INITIALTIMEOUTFACTOR 4 |
| #define MAXTIMEOUTFACTOR 20 |
| static int timeoutfactor = INITIALTIMEOUTFACTOR; |
| |
| /* Routines used internally. */ |
| static void plip_device_clear(struct device *dev); |
| static void plip_receiver_error(struct device *dev); |
| static void plip_set_physicaladdr(struct device *dev, unsigned long ipaddr); |
| static int plip_addrcmp(struct ethhdr *eth); |
| static int plip_send_enethdr(struct device *dev, struct ethhdr *eth); |
| static int plip_rebuild_enethdr(struct device *dev, struct ethhdr *eth, |
| unsigned char h_dest, unsigned char h_source, |
| unsigned short type); |
| static void cold_sleep(int tics); |
| static void plip_interrupt(int reg_ptr); /* Dispatch from interrupts. */ |
| static int plip_receive_packet(struct device *dev); |
| static int plip_send_packet(struct device *dev, unsigned char *buf, int length); |
| static int plip_send_start(struct device *dev, struct ethhdr *eth); |
| static void double_timeoutfactor(void); |
| static struct enet_statistics *plip_get_stats(struct device *dev); |
| |
| int |
| plip_init(struct device *dev) |
| { |
| int port_base = dev->base_addr; |
| int i; |
| |
| /* Check that there is something at base_addr. */ |
| outb(0x00, port_base + PAR_CONTROL); |
| outb(0x55, port_base + PAR_DATA); |
| if (inb(port_base + PAR_DATA) != 0x55) |
| return -ENODEV; |
| |
| /* Alpha testers must have the version number to report bugs. */ |
| #ifdef PLIP_DEBUG |
| { |
| static int version_shown = 0; |
| if (! version_shown) |
| printk(version), version_shown++; |
| } |
| #endif |
| |
| /* Initialize the device structure. */ |
| dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL); |
| memset(dev->priv, 0, sizeof(struct netstats)); |
| |
| for (i = 0; i < DEV_NUMBUFFS; i++) |
| dev->buffs[i] = NULL; |
| dev->hard_header = &plip_header; |
| dev->add_arp = eth_add_arp; |
| dev->queue_xmit = dev_queue_xmit; |
| dev->rebuild_header = eth_rebuild_header; |
| dev->type_trans = eth_type_trans; |
| |
| dev->open = &plip_open; |
| dev->stop = &plip_close; |
| dev->hard_start_xmit = &plip_tx_packet; |
| dev->get_stats = &plip_get_stats; |
| |
| /* These are ethernet specific. */ |
| dev->type = ARPHRD_ETHER; |
| dev->hard_header_len = ETH_HLEN; |
| dev->mtu = PLIP_MTU; /* PLIP may later negotiate max pkt size */ |
| dev->addr_len = ETH_ALEN; |
| for (i = 0; i < dev->addr_len; i++) { |
| dev->broadcast[i]=0xff; |
| dev->dev_addr[i] = 0; |
| } |
| printk("%s: configured for parallel port at %#3x, IRQ %d.\n", |
| dev->name, dev->base_addr, dev->irq); |
| |
| /* initialize internal value */ |
| timeoutfactor = INITIALTIMEOUTFACTOR; |
| return 0; |
| } |
| |
| /* Open/initialize the board. This is called (in the current kernel) |
| sometime after booting when the 'config <dev->name>' program is |
| run. |
| |
| This routine gets exclusive access to the parallel port by allocating |
| its IRQ line. |
| */ |
| static int |
| plip_open(struct device *dev) |
| { |
| if (dev->irq == 0) |
| dev->irq = 7; |
| cli(); |
| if (request_irq(dev->irq , &plip_interrupt) != 0) { |
| sti(); |
| PRINTK(("%s: couldn't get IRQ %d.\n", dev->name, dev->irq)); |
| return -EAGAIN; |
| } |
| |
| irq2dev_map[dev->irq] = dev; |
| sti(); |
| plip_device_clear(dev); |
| dev->tbusy = 0; |
| dev->interrupt = 0; |
| dev->start = 1; |
| return 0; |
| } |
| |
| /* The inverse routine to plip_open(). */ |
| static int |
| plip_close(struct device *dev) |
| { |
| dev->tbusy = 1; |
| dev->start = 0; |
| cli(); |
| free_irq(dev->irq); |
| irq2dev_map[dev->irq] = NULL; |
| sti(); |
| outb(0x00, dev->base_addr); /* Release the interrupt. */ |
| return 0; |
| } |
| |
| static int |
| plip_tx_packet(struct sk_buff *skb, struct device *dev) |
| { |
| int ret_val; |
| |
| if (dev->tbusy || dev->interrupt) { /* Do timeouts, to avoid hangs. */ |
| int tickssofar = jiffies - dev->trans_start; |
| if (tickssofar < 50) |
| return 1; |
| printk("%s: transmit timed out\n", dev->name); |
| /* Try to restart the adaptor. */ |
| plip_device_clear(dev); |
| return 0; |
| } |
| |
| /* If some higher layer thinks we've missed an tx-done interrupt |
| we are passed NULL. Caution: dev_tint() handles the cli()/sti() |
| itself. */ |
| if (skb == NULL) { |
| dev_tint(dev); |
| return 0; |
| } |
| |
| /* Pretend we are an ethernet and fill in the header. This could use |
| a simplified routine someday. */ |
| if (!skb->arp && dev->rebuild_header(skb->data, dev)) { |
| skb->dev = dev; |
| arp_queue (skb); |
| return 0; |
| } |
| skb->arp=1; |
| |
| dev->trans_start = jiffies; |
| ret_val = plip_send_packet(dev, skb->data, skb->len); |
| if (skb->free) |
| kfree_skb (skb, FREE_WRITE); |
| dev->tbusy = 0; |
| mark_bh (INET_BH); |
| return 0/*ret_val*/; |
| } |
| |
| static int |
| plip_header (unsigned char *buff, struct device *dev, |
| unsigned short type, unsigned long h_dest, |
| unsigned long h_source, unsigned len) |
| { |
| if (dev->dev_addr[0] == 0) { |
| /* set physical address */ |
| plip_set_physicaladdr(dev, h_source); |
| } |
| return eth_header(buff, dev, type, h_dest, h_source, len); |
| } |
| |
| static void |
| plip_device_clear(struct device *dev) |
| { |
| dev->interrupt = 0; |
| dev->tbusy = 0; |
| outb(0x00, dev->base_addr + PAR_DATA); |
| outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ |
| } |
| |
| static void |
| plip_receiver_error(struct device *dev) |
| { |
| dev->interrupt = 0; |
| dev->tbusy = 0; |
| outb(0x02, dev->base_addr + PAR_DATA); |
| outb(0x10, dev->base_addr + PAR_CONTROL); /* Enable the rx interrupt. */ |
| } |
| |
| static int |
| get_byte(struct device *dev) |
| { |
| unsigned char val, oldval; |
| unsigned char low_nibble; |
| int timeout; |
| int error = 0; |
| val = inb(dev->base_addr + PAR_STATUS); |
| timeout = jiffies + timeoutfactor * 2; |
| do { |
| oldval = val; |
| val = inb(dev->base_addr + PAR_STATUS); |
| if ( oldval != val ) continue; /* it's unstable */ |
| if ( timeout < jiffies ) { |
| error++; |
| break; |
| } |
| } while ( (val & 0x80) ); |
| val = inb(dev->base_addr + PAR_STATUS); |
| low_nibble = (val >> 3) & 0x0f; |
| outb(0x11, dev->base_addr + PAR_DATA); |
| timeout = jiffies + timeoutfactor * 2; |
| do { |
| oldval = val; |
| val = inb(dev->base_addr + PAR_STATUS); |
| if (oldval != val) continue; /* it's unstable */ |
| if ( timeout < jiffies ) { |
| error++; |
| break; |
| } |
| } while ( !(val & 0x80) ); |
| val = inb(dev->base_addr + PAR_STATUS); |
| PRINTK2(("%02x %s ", low_nibble | ((val << 1) & 0xf0), |
| error ? "t":"")); |
| outb(0x01, dev->base_addr + PAR_DATA); |
| if (error) { |
| /* timeout error */ |
| double_timeoutfactor(); |
| return -1; |
| } |
| return low_nibble | ((val << 1) & 0xf0); |
| } |
| |
| /* The typical workload of the driver: |
| Handle the parallel port interrupts. */ |
| static void |
| plip_interrupt(int reg_ptr) |
| { |
| int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2); |
| struct device *dev = irq2dev_map[irq]; |
| struct netstats *localstats; |
| |
| if (dev == NULL) { |
| PRINTK(("plip_interrupt(): irq %d for unknown device.\n", irq)); |
| return; |
| } |
| localstats = (struct netstats*) dev->priv; |
| if (dev->tbusy || dev->interrupt) return; |
| dev->interrupt = 1; |
| outb(0x00, dev->base_addr + PAR_CONTROL); /* Disable the rx interrupt. */ |
| sti(); /* Allow other interrupts. */ |
| PRINTK2(("%s: interrupt. ", dev->name)); |
| |
| { |
| /* check whether the interrupt is valid or not.*/ |
| int timeout = jiffies + timeoutfactor; |
| while ((inb(dev->base_addr + PAR_STATUS) & 0xf8) != 0xc0) { |
| if ( timeout < jiffies ) { |
| PRINTK2(("%s: No interrupt (status=%#02x)!\n", |
| dev->name, inb(dev->base_addr + PAR_STATUS))); |
| plip_device_clear(dev); |
| return; |
| } |
| } |
| } |
| if (plip_receive_packet(dev)) { |
| /* get some error while receiving data */ |
| localstats->rx_errors++; |
| plip_receiver_error(dev); |
| } else { |
| plip_device_clear(dev); |
| } |
| } |
| |
| static int |
| 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 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*)ð->h_dest[2],*(short*)ð->h_dest[0], |
| *(long*)ð->h_source[2],*(short*)ð->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: |
| */ |