?? plip.c
字號:
/* 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 octetEach 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 asa "LapLink" cable by various places. You'll need a 10-conductor cable tomake 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 20static 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);intplip_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 intplip_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 intplip_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 intplip_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 intplip_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
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -