root/net/inet/README.DRIVERS

/* [previous][next][first][last][top][bottom][index][help] */
This file contains some info on writing device driver for the
NET-2 networking suite of the Linux operating system.

Basically, have a look at the "loopback.c" and "slip.c" drivers
to get a feel of what is going on, and then use the code below
as a skeleton of the new driver you are writing.

=============================================================================
/* network.c: A sample network driver core for linux. */
/*
    Written 1993 by Donald Becker.
    Copyright 1993 United States Government as represented by the Director,
    National Security Agency.  This software may only be used and distributed
    according to the terms of the GNU Public License as modified by SRC,
    incorported herein by reference.

    The author may be reached as becker@super.org or
    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/

static char *version =
    "network.c:v0.02 Donald Becker (becker@super.org)\n";

/* Always include 'config.h' first in case the user wants to turn on
   or override something. */
#include <linux/config.h>

/*
  Sources:
        List your sources of programming information to document that
        the driver is your own creation, and give due credit to others
        that contributed to the work.  Remember that GNU project code
        cannot use proprietary or trade secret information.  Interface
        definitions are generally considered non-copyrightable to the
        extent that the same names and structures must be used to be
        compatible. */

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ptrace.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/in.h>

#include "inet.h"
#include "dev.h"
#include "eth.h"
#include "timer.h"
#include "ip.h"
#include "route.h"
#include "protocol.h"
#include "tcp.h"
#include "skbuff.h"
#include "sock.h"
#include "arp.h"

/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 2
#endif
static unsigned int net_debug = NET_DEBUG;

/* The map from IRQ number (as passed to the interrupt handler) to
   'struct device'. */
extern struct device *irq2dev_map[16];

/* Common network statistics -- these will be in *.h someday. */
struct netstats {
    int tx_packets;
    int rx_packets;
    int tx_errors;
    int rx_errors;
    int missed_packets;
    int soft_tx_errors;
    int soft_rx_errors;
    int soft_trx_err_bits;
};
static struct netstats *localstats;

/* Index to functions, as function prototypes. */
/* Put in the device structure. */
static int net_open(struct device *dev);
static void net_send_packet(struct sk_buff *skb, struct device *dev);
/* Dispatch from interrupts. */
static void net_interrupt(int reg_ptr);
static void net_tx_intr(struct device *dev);
static void net_rx_intr(struct device *dev);
/* Routines used internally. */
static void chipset_init(struct device *dev, int startp);
static void trigger_send(struct device *dev, unsigned int length,
                                int start_page);
extern int netcard_probe(int ioaddr, struct device *dev);


/* Open/initialize the board.  This is called (in the current kernel)
   sometime after booting when the 'config <dev->name>' program is
   run.

   This routine should set everything up anew at each open, even
   registers that "should" only need to be set once at boot, so that
   there is non-reboot way to recover if something goes wrong.
   */
static int
net_open(struct device *dev)
{
  if ( ! net_status.exists) {
      printk("%s: Opening a non-existent physical device\n",
             dev ? dev->name : "(null)");
      return ENXIO;             /* Anything non-zero will do. */
  }

  chipset_init(dev, 1);

  /* If the IRQ line can be software selected find a free line to use. */
  if (dev->irq < 2) {
      int irq_list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
      int *irq = irq_list;
      for (; *irq; irq++) {
          if (request_irq(dev->irq = *irq, &net_interrupt) == 0)
              break;
      }
      if (*irq == 0) {
          printk (" unable to get an IRQ.\n");
          return EAGAIN;
      }
      card_set_irq(dev, *irq);
  }

  irq2dev_map[irq] = dev;
  dev->tbusy = 0;               /* Transmit busy...  */
  dev->interrupt = 0;
  dev->start = 1;
  return 0;
}

/* The inverse routine to net_open(). */
static int
net_close(struct device *dev)
{
    dev->start = 0;
    card_flush_tx(dev);
    card_disable_reciever(dev);
    irq2dev_map[dev->irq] = NULL;
    /* If not IRQ jumpered, free up the interrupt line. */
    card_set_irq(0);
    free_irq(dev->irq);
    return 0;
}

static int
net_start_xmit(struct sk_buff *skb, struct device *dev)
{

    if (dev->tbusy) {   /* Do timeouts, to avoid hangs. */
        int tickssofar = jiffies - dev->trans_start;
        if (tickssofar < 5)
            return 1;
        printk("%s: transmit timed out, %s?\n", dev->name,
               tx_done(dev) ? "IRQ conflict" : "network cable problem");
        /* Try to restart the adaptor. */
        chipset_init(dev, 1);
    }

    /* 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;
    }

    /* For ethernet, fill in the header. */
    if (!skb->arp  &&  dev->rebuild_header(skb+1, dev)) {
        skb->dev = dev;
        arp_queue (skb);
        return 0;
    }

    dev->trans_start = jiffies;
    cli();
    net_send_packet(skb, dev);
    sti();
    if (skb->free)
        kfree_skb (skb, FREE_WRITE);
    return 0;
}

/* The typical workload of the driver:
   Handle the network interface interrupts. */
static void
net_interrupt(int reg_ptr)
{
    int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
    struct device *dev = irq2dev_map[irq];
    int interrupts, boguscount = 0;

    if (dev == NULL) {
        printk ("net_interrupt(): irq %d for unknown device.\n", irq);
        return;
    }
    dev->interrupt = 1;
    sti(); /* Allow other interrupts. */

    if (net_debug >= 4)
        printk("%s: interrupt.\n", dev->name);

    localstats = (struct netstats*) dev->private;

    do {
        interrupts = chip_read_status(dev);
        if (interrupts & OVERRUN_INTR) {
            localstats->missed_packets++;
            net_rx_overrun(dev);
        } else if (interrupts & RX_INTR) {
            /* Got a good (?) packet. */
            net_receive(dev);
            localstats->rx_packets++;
        }
        if (interrupts & TX_INTR) {
            /* Push the next to-transmit packet through. */
            net_tx_intr(dev);
        } else if (interrupts & COUNTERS_INTR) {
            /* Increment the appropriate 'localstats' field. */
        }
    } while (++boguscount < 20) ;

    if (interrupts && net_debug) {
      printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
    }
    return;
}

/* We have finished a transmit: check for errors and then mark that
   the bottom half has some work to do. */
static void
net_tx_intr(struct device *dev)
{
    int status = card_get_status(dev);
    if (status & TX_OK)
        tx_packets++;
    else {
        tx_errors++;
        card_reset(dev);
    }
    dev->tbusy = 0;
    mark_bh (INET_BH);
}

/* We have a good packet(s), get it/them out of the buffers. */
static void
net_receive(struct device *dev)
{
    int boguscount = 0;

    do {
        int size = chip_get_rx_size(dev);
        int sksize;
        struct sk_buff *skb;
      
        if (size == 0)          /* Read all the frames? */
            break;                      /* Done for now */
      
        if ((size < 32  ||  size > dev->mtu) && net_debug)
            printk("%s: Bogus packet size %d.\n", dev->name, size);
      
        sksize = sizeof(struct sk_buff) + size;
        skb = kmalloc(sksize, GFP_ATOMIC);
        if (skb != NULL) {
            skb->lock = 0;
            skb->mem_len = sksize;
            skb->mem_addr = skb;
            /* 'skb+1' points to the start of sk_buff data area. */
            card_get_packet(dev, size, (void *)(skb+1));
            if(dev_rint((void *)skb, size, IN_SKBUFF, dev)) {
                printk("%s: receive buffers full.\n", dev->name);
                break;
            }
        } else if (net_debug) {
            printk("%s: Couldn't allocate a sk_buff of size %d.\n",
                   dev->name, sksize);
            break;
        }
        rx_packets++;
    } while (++boguscount < 10);

    /* If any worth-while packets have been received, dev_rint()
       has done a mark_bh(INET_BH) for us and will work on them
       when we get to the bottom-half routine. */
    return;
}

/* We have a receiver overrun: do whatever this chipset needs
 to recover. */
static void
net_rx_overrun(struct device *dev)
{
    int reset_start_time = jiffies;
    if (net_debug)
        printk("%s: Receiver overrun.\n", dev->name);
    return;
}

int
netdev_init(struct device *dev)
{
    int i, found = 0;
    int *port, ports[] = {0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};

    /* Alpha testers must have the version number to report bugs. */
    if (net_debug > 1)
        printk(version);

    /*  Probe for an adaptor at a likely set of locations. */
    if (dev->base_addr > 0x100) {
        if (card_probe_addr(dev, ioaddr) == 0) {
            printk("%s: probe failed at %#3x.\n", dev->name, ioaddr);
            return ENODEV;
        }
    } else {
        for (port = &ports[0]; *port; port++)
            if (card_probe_addr(dev, *port))
                break;
        if (*port == 0) {
            dev->open = NULL;
            printk("No network device found.\n");
            return ENODEV;
        }
    }

    /* Initialize the device structure. */
    dev->private = kmalloc(sizeof(struct netstats), GFP_KERNEL);
    memset(dev->private, 0, sizeof(struct netstats));
    for (i = 0; i < DEV_NUMBUFFS; i++)
        dev->buffs[i] = NULL;

    dev->type = ETHER_TYPE;
    for (i = 0; i < dev->addr_len; i++) {
        dev->broadcast[i]=0xff;
    }
    card_get_station_address(dev->dev_addr);

    /* Finish setting up the DEVICE info. */
    dev->queue_xmit     = dev_queue_xmit;
    dev->open           = net_open;
    dev->stop           = net_close;
    dev->hard_start_xmit= net_start_xmit;

    /* These are ethernet specific. */
    dev->type           = ARPHRD_ETHER;
    dev->mtu            = 1500; /* eth_mtu */
    dev->hard_header    = eth_header;
    dev->add_arp        = eth_add_arp;
    dev->rebuild_header = eth_rebuild_header;
    dev->type_trans     = eth_type_trans;
    dev->hard_header_len= ETH_HLEN;
    dev->addr_len       = ETH_ALEN;

    /* New-style flags. */
    dev->flags          = 0;
    dev->family         = AF_INET;
    dev->pa_addr        = 0;
    dev->pa_brdaddr     = 0;
    dev->pa_mask        = 0;
    dev->pa_alen        = sizeof(unsigned long);

#ifdef jumpered_interrupts
  /* If this board has jumpered interrupts, snarf the interrupt vector
     now.  There is no point in waiting since no other device can use
     the interrupt, and this marks the 'irqaction' as busy. */

  if (dev->irq == -1)
      ;                 /* Do nothing: a user-level program will set it. */
  else if (dev->irq < 2) {      /* "Auto-IRQ" */
      autoirq_setup(0);
      /* Trigger an interrupt here. */
      dev->irq = autoirq_report(0);
      if (net_debug >= 2)
          printk(" autoirq is %d", dev->irq);
  } else if (dev->irq == 2)
      /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
         or don't know which one to set. */
      dev->irq = 9;

  {    int irqval = request_irq(dev->irq, &net_interrupt);
       if (irqval) {
           printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
                   dev->irq, irqval);
           return 0;
       }
   }
#endif  /* jumpered interrupt */
    printk("%s: %s found at %#3x, IRQ %d.\n", dev->name,
           "network card", dev->base_addr, dev->irq);

    return 0;
}

/* Check IOADDR for a network adaptor of this type, and return
   a non-zero value iff one exists. */
static int
card_probe_addr(struct device *dev, int ioaddr)
{
  if (inb_p(ioaddr) == 0xFF) {  /* Trivial check. */
      return 0;
  }

  /* Check for the card here.  You should minimize outb()s, and
     restore the previous value if possible. */
  if (/*probe result */1 == 0)
      return 0;

  return dev->base_addr = ioaddr;
}

/*
 * Local variables:
 *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c network.c"
 *  version-control: t
 *  kept-new-versions: 5
 * End:
 */
===============================================================================

/* [previous][next][first][last][top][bottom][index][help] */