📄 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 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:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -