Source Code

We will now go over the source code of our driver.  A network driver stores packet information   in the net_device data structure. This data structure  is defined in  <linux/netdevice.h>. Basically, the net_device structure has fields and a set of callback functions. These fields and callback functions must be initialized by the driver.  The  file drivers/net/Space.c     is responsible for detect and initialize network devices. 

As explained in section 5 , we use the  character count framing method for our driver.  I designed the header of the packet to be   4 bytes long which  contains the size of the packet. Let's say the value in the header is 100, then the data frame's size is 100.
.figure 6
 
The problem with this design is that if a byte in the header flips during transmission, the driver will be  out of synchronization at  the destination and  we will have to restart the system. Let's say the header of a packet contains the length of 100 at the sender's end, when  it arrives at the destination, a byte in the header flips, the header now contains 110 instead of 100 at the receiver's end. Clearly, this puts the driver out of sync. So what do we do about this problem? Nothing. You could change the driver to use starting and ending character at each frame, but you will have to do some research on this.

The source of the driver  contains five  files: fifo.c, fifo.h, z85230.c, z85230.h and Makefile. Below is the  Makefile for our driver.

 Makefile:

  1. INCLUDEDIR = /usr/src/linux/include
  2. CFLAGS = -D__KERNEL__ -DMODULE -O -I$(INCLUDEDIR)
  3. OBJS    = fifo.o z85230.o
  4. IP    = 192.168.2.1     # give a different IP address to the second computer, say 192.168.2.2
  5. NM     = 255.255.255.0
  6. fifodev.o: $(OBJS)
  7.      $(LD) -r $^ -o $@
  8. fifo.o: fifo.c fifo.h
  9. z85230.o: z85230.c z85230.h
  10. load:
  11.     insmod fifodev.o port=0x300 irq=10
  12.     ifconfig fifo0 $(IP)
  13.     route add -net 192.168.2.0 netmask $(NM) dev fifo0
  14.     ifconfig fifo0
  15. unload:
  16.     ifconfig fifo0 down
  17.     rmmod fifodev
  18. clean:
  19.     rm -f $(OBJS)
To compile the driver, type make.  This will produce a file called fifodev.o.  The next step is to load the driver into the kernel.  By  typing make load, we execute line 10 to 14. Basically,  we load  the driver into the kernel with I/O base  address of  0x300 and  irq 10 using  the insmod  command.  The ifconfig command sets  the IP address to the network interface fifo0. The route command adds a route in the static routing table. To unload the driver from  the kernel, use make unload .

In summary, the following commands are used to operate our driver.
 [root@peanut /root] make   - compile the driver
 [root@peanut /root] make load - load the driver into the kernel and add the IP address to the network interface.
 [root@peanut /root] make unload - unload the driver.
 [root@peanut /root] make clean  - clean all obj files.
 After you load the driver, you can test it by using the ping  command:
 [root@peanut /root] ping  192.168.2.1
PING Walnut (192.168.2.1): 56 data bytes
64 bytes from  192.168.2.1: icmp_seq=0 ttl=255 time=0.2 ms
64 bytes from  192.168.2.1: icmp_seq=1 ttl=255 time=0.0 ms
64 bytes from  192.168.2.1: icmp_seq=2 ttl=255 time=0.0 ms


 z85230.h:
This file contains register descriptions of the Zilog ESCC.   

z85230.c
This file contains I/O ports. These ports are used to read, write to the hardware. Under Linux, I/O ports are defined in  <asm/io.h>. The size of the Zilog ESCC registers is 8 bits wide, so  we  use inb() and outb() to read and write to registers respectively. I have defined these functions to  access to theZilog ESCC:

void scc_outp(int port,int value);  
int scc_inp(int port);
        Read or write value to registers.

void scc_putch(char ch);
       Write a character to Channel A tx fifo.

void scc_puts(char *str,int len);
      Write a string to Channel A tx fifo.

int scc_getch(void);
    Read a character from Channel rx fifo if there's one there.
    Returns the character read or -1 if fifo empty .

void scc_setup ();
    Get the z85230 into a vaguely sensible state .
    All values pinched from Sealevel diagnostics.

void scc_reset ();
    Reset the z85230.

void iodelay();
    The Zilog controller needs to be delay by 5ms between each register access.  
   
fifo.h
This file contains prototypes for fifo.c .

fifo.c

int init_module(void);
This is the entry point to the driver. This function calls check_region() to verify  if the range of ports is already locked by other devices. The register_netdev() function is responsible for registering  the net_device structure with the kernel. 

void cleanup_module(void);
This function is the driver exit.  It calls  unregister_netdev() to remove the net_device structure from the kernel. A compliant network driver always calls register_netdev() and unregister_netdev().

int fifo_open (struct net_device *dev);
When you use the command  ifconfig fifo0 up,  this callback function will be called by the driver.   This function is responsible to register the interrupt handler and  requesting the I/O ports with the kernel.  The request_irq() is an important function. It gets your interrupt handler  called when the Zilog ESCC receives an interrupt acknowledgement from the CPU.  The request_irq() needs the following arguments:

unsigned int irq
    Linux IRQ number.  Under x86 architecture, the Linux IRQ number is the same as the hardware number that you set with the jumper  on Sealevel card.

void  (*handler)(int, void *, struct pt_regs *)
     Pointer to  the interrupt handler function.

 unsigned long flags
      The flags can be SA_INTERRUPT for fast interrupt handler, SA_SHIRQ for share interrupt.    
 
const char *device
       Name of  the interrupt that is used in  /proc/interrupt

void*dev_id
       A parameter to pass to the interrupt handler.

int fifo_stop (struct net_device *dev)
This function disable the Zilog ESCC and free the IRQ and ports.

int fifo_tx (struct sk_buff *skb, struct net_device *dev)
This function transmits packets to the RS232 interface.  The hard_start_xmit method of the net_device structure points to fifo_tx, which is responsible for  putting  the data on the wire. The fifo_tx() function calls fifo_transmit()

void fifo_irq_handler(int irq, void *dev_id, struct pt_regs *regs);
This is our interrupt handler.  It is triggered  whenever a byte arrived  in the FIFO. I set the ESCC to interrupt every time it receives a byte.

Conclusion:
I hope that you enjoy reading this tutorial.  If you want to learn more about device driver, you  could change  this driver to use DMA instead of interrupt.   

Trouble-shooting:
If you have problems with the driver, please follow these steps to solve your problems:


Previous                                              Index                                                  Next